Сделайте компонент повторно используемым с Typescript в React - PullRequest
0 голосов
/ 19 апреля 2019

Создавая приложение React, я хочу сделать свой код максимально сухим. Я впервые начал использовать Typescript в этом проекте, и я столкнулся с проблемой повторного использования моих компонентов, где JSX может быть одинаковым для разных случаев, но типы и интерфейсы меняются.

Вот реальный пример: приведенный ниже код представляет собой компонент React, созданный с использованием TypeScript, Apollo / GraphQL и Redux. Он возвращает таблицу всех футбольных команд в моей БД, полученную из бэкэнд-API.

Дело в том, что я хотел бы использовать этот же компонент для отображения таблицы игр, таблицы игроков и т. Д.

И я застрял, например, с моим Team интерфейсом. Каждая команда имеет только id и name; но каждая игра имеет date, homeTeam и awayTeam, a Score` и т. д.

Следовательно, как я могу управлять своими интерфейсами и типами, чтобы я мог повторно использовать этот компонент?

import React from 'react';
import { connect } from 'react-redux';
import { setView, toggleDeleteElemModal } from '../../redux/actions';

import gql from 'graphql-tag';
import { Query } from 'react-apollo';
import { ApolloError } from 'apollo-client';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faInfo, faEdit, faTrash } from '@fortawesome/free-solid-svg-icons';

interface SectionTableProps {
  setView: typeof setView
  toggleDeleteElemModal : typeof toggleDeleteElemModal
}

interface Team {
  id: string;
  name: string;
}

interface Data {
  allTeams?: Team[];
}

interface SectionTableQueryProps {
    allTeams: Team[];
    error?: ApolloError;
    loading: boolean;
}

const GET_ALL_TEAMS = gql`
  query {
    allTeams {
      id
      name
    }
  }
`;

class SectionTableQuery extends Query<Data, {}> {}

const SectionTable = (props: SectionTableProps) => (
  <table className="table table-hover table-responsive">
    <thead>
      <tr>
        <th scope="col">ID</th>
        <th scope="col">Name</th>
        <th scope="col">Actions</th>
      </tr>
    </thead>
      <SectionTableQuery query={GET_ALL_TEAMS}>
      {({ data: { allTeams = [] } = {}, error, loading }) => {
        if (loading) {
          return <tbody><tr><td>LOADING</td></tr></tbody>
        };
        if (error !== undefined) {
          return <tbody><tr><td>ERROR</td></tr></tbody>
        };
        return (
          <tbody>
            {allTeams.map((team: Team) => (
                <tr key={team.id}>
                  <th scope="row">{team.id}</th>
                  <td>{team.name}</td>
                  <td className="d-flex justify-content-between">
                  <div onClick={() => props.setView("info")}>
                    <FontAwesomeIcon icon={faInfo} />
                  </div>
                  <div onClick={() => props.setView("edit")}>
                    <FontAwesomeIcon icon={faEdit} />
                  </div>
                  <div onClick={() => props.toggleDeleteElemModal()}>
                    <FontAwesomeIcon icon={faTrash} />
                  </div>
                  </td>
                </tr>

            ))}
          </tbody>
        );
      }}
    </SectionTableQuery>
  </table>
)

const mapDispatchToProps = { setView, toggleDeleteElemModal }

export default connect(null, mapDispatchToProps)(SectionTable);

1 Ответ

1 голос
/ 19 апреля 2019

Для этого поведения можно использовать универсальные типы для компонентов JSX.

class SectionTableQuery extends Query<P, {}> {} // where P is the generic prop

Объявление SectionTable должно принимать этот общий P интерфейс от своего родителя и при отображении SectionTableQuery:

const SectionTable<P> = (props: SectionTableProps) => (
  <SectionTableQuery<P> query={props.query}>  // Assuming you want the query to change, that would be passed as a prop too
  ...
  </SectionTableQuery>
);

Во время рендеринга SectionTable родитель может передать реквизиты, которые он / она хочет

<SectionTable<ITeam> ... />
<SectionTable<IPlayer> ... />

Это общее приближение к тому, что вам нужно сделать, похоже, может быть несколько общих типов, которые вы хотите передать в зависимости от запроса (IData для того, что возвращается из запроса, и IItem для самого предмета`)

PS: Общие компоненты доступны с Машинопись 2.9

...