Создавая приложение React, я хочу сделать свой код как можно более СУХИМ. Я впервые начал использовать Typescript в этом проекте и столкнулся с проблемой повторного использования моих компонентов, где JSX может быть одинаковым для разных случаев, но меняются типы и интерфейсы.
Вот реальный пример: приведенный ниже код представляет собой компонент React, созданный с помощью TypeScript, Apollo/GraphQL и Redux. Он возвращает таблицу всех футбольных команд в моей БД, полученную из внутреннего 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);





Вы можете использовать общие типы для компонентов 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.
Спасибо за помощь ! Действительно, Generics — это волшебство, которое я искал.