在React中使用Typescript使组件可重用

时间:2019-04-19 10:25:38

标签: reactjs typescript graphql apollo

构建一个React应用程序,我想让我的代码尽可能的干燥。我在该项目中第一次开始使用Typescript,但是我遇到了组件可重用性的问题,其中JSX在不同情况下可以相同,但是类型和接口会发生变化。

这是一个真实的例子:下面的代码是一个使用TypeScript,Apollo / GraphQL和Redux构建的React组件。它返回从后端API获取的我数据库中所有足球队的表格。

问题是,我想使用相同的组件来显示游戏表,玩家表等。

例如,我被Team界面所困扰。每个团队只有一个id和一个name;但每个游戏都有一个date,一个homeTeam和一个awayTeam, a分数等等。

因此,如何管理接口和类型,以便可以重用此组件?

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 个答案:

答案 0 :(得分:1)

您可以将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是查询返回的内容,{ {1}}(商品本身))

PS:通用组件可从Typescript 2.9

获得