在 React 中使用 Typescript 使组件可重用
Make Component Reusable with Typescript in React
构建一个 React 应用程序,我想让我的代码尽可能干。我在这个项目中第一次开始使用 Typescript,我面临着组件的可重用性问题,其中 JSX 对于不同的情况可能是相同的,但类型和接口会发生变化。
这是一个真实的例子:下面的代码是一个使用 TypeScript、Apollo / GraphQL 和 Redux 构建的 React 组件。它 returns 我数据库中所有足球队的 table,从后端 API 获取。
问题是,我想使用同一个组件来显示 table 个游戏、table 个玩家等。
例如,我被 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);
您可以为此行为使用 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
时,家长可以选择传入he/she想要的道具
<SectionTable<ITeam> ... />
<SectionTable<IPlayer> ... />
这是对您必须执行的操作的一般近似,看起来可能有不止一种泛型类型要传入,具体取决于查询(IData
对于从查询返回的内容IItem
用于项目本身`)
PS:通用组件可从 Typescript 2.9
获得
构建一个 React 应用程序,我想让我的代码尽可能干。我在这个项目中第一次开始使用 Typescript,我面临着组件的可重用性问题,其中 JSX 对于不同的情况可能是相同的,但类型和接口会发生变化。
这是一个真实的例子:下面的代码是一个使用 TypeScript、Apollo / GraphQL 和 Redux 构建的 React 组件。它 returns 我数据库中所有足球队的 table,从后端 API 获取。
问题是,我想使用同一个组件来显示 table 个游戏、table 个玩家等。
例如,我被 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);
您可以为此行为使用 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
时,家长可以选择传入he/she想要的道具
<SectionTable<ITeam> ... />
<SectionTable<IPlayer> ... />
这是对您必须执行的操作的一般近似,看起来可能有不止一种泛型类型要传入,具体取决于查询(IData
对于从查询返回的内容IItem
用于项目本身`)
PS:通用组件可从 Typescript 2.9
获得