React 组件中的 TypeScript 泛型
Source: Dev.to
介绍
泛型并不是在 React 组件中每天都会用到的东西,但在某些情况下,它们可以让你编写既灵活又类型安全的组件。
基础组件定义
不带 Props
// Just FC component with no props
const Component = () => Hey ya!!!;
使用 React.FC 的 Props
// FC component with props
interface Props {
name: string;
}
const Component: React.FC = ({ name }) => (
Hey {name} ya!!!
);
React.FC因为一些原因已经不太受欢迎,其中之一是它无法以我们有时需要的方式处理泛型。不过,很多开发者仍然因为可读性而继续使用它。
不使用 React.FC
// FC component with simple props
interface Props {
name: string;
}
const Component = ({ name }: Props) => Hey {name} ya!!!;
何时需要泛型
考虑一种情况:组件从 API 接收一个包含数组的 DTO(数据传输对象)。组件需要显示一个代表“名称”的属性,但不同 DTO 中的属性名可能不同。
示例 DTO
interface ResponseDTO {
id: number;
name: string;
}
与特定 DTO 绑定的组件
interface ExampleProps {
data: ResponseDTO[];
getName: (item: ResponseDTO) => string;
}
export const Example = ({
data,
getName = (item) => item.name,
}: ExampleProps) => (
{data.map((item) => (
{getName(item)}
))}
);
因为组件知道 ResponseDTO 的确切结构,这段代码可以工作。如果我们希望组件能够处理 任何 具有“类似名称”值的 DTO,就需要使用泛型。
泛型 Props 接口
interface Props {
data: T[];
getName: (item: T) => string;
}
泛型参数 T 将 data 数组和 getName 回调绑定在一起,确保它们操作的是同一种类型。
泛型组件
export const NameViewer = ({ data, getName }: Props) => (
<>
Look at the names:
{data.map((item) => (
{getName(item)}
))}
);
注意: 在
.tsx文件中,写成 “(尾随逗号)可以将泛型声明与 JSX 语法区分开来。
使用泛型组件
const unknownTypeData = [
{ id: 1, nameProp: 'John' },
{ id: 2, nameProp: 'Jane' },
];
const anotherUnknownTypeData = ['Foo', 'Bar'];
export const BusinessComponent = () => (
item.nameProp}
/>
item}
/>
);
只要提供合适的 getName 函数,NameViewer 现在就可以接受任何类型的数据。
结论
这个简单的例子展示了泛型如何让 React 组件能够适配各种数据结构,同时保持类型安全。在后续文章中,我们将探讨更高级的模式,例如条件泛型。