随着 TypeScript 在 React 生态系统中的采用率不断提高,了解高级模式可以显著提高您的代码质量、可扩展性和开发人员体验。本文重点介绍了 React 开发的基本 TypeScript 模式,以及帮助你充分利用这些工具的实际示例。
Discriminated unions (管理复杂的状态(数据))
Discriminated unions 是处理具有多个变体或 "模式" 的状态的有效方法。通过将联合类型与 "discriminator" 属性组合在一起,您可以确保类型安全并避免由无效状态引起的错误。
示例 状态加载
ts
type FetchState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: string[] }
| { status: 'error'; error: string };
const fetchReducer = (state: FetchState, action: any): FetchState => {
switch (action.type) {
case 'FETCH_START':
return { status: 'loading' };
case 'FETCH_SUCCESS':
return { status: 'success', data: action.payload };
case 'FETCH_ERROR':
return { status: 'error', error: action.payload };
default:
return state;
}
};
为什么有用 Discriminated unions 允许您的状态转换是显式的,从而避免无效的组合,例如同时具有 data
和 error
。
泛型:定义具有复用性和适应性的组件
泛型使你的组件或 hook 可重用,同时保留强类型定义。这对于处理具有不同数据结构的列表、表单或 API 特别有用。
示例, 可复用的表格组件
ts
type TableProps<T> = {
data: T[];
renderRow: (item: T) => React.ReactNode;
};
function Table<T>({ data, renderRow }: TableProps<T>) {
return (
<table>
<tbody>
{data.map((item, index) => <tr key={index}>{renderRow(item)}</tr>)}
</tbody>
</table>);
}
// Usage
type User = { id: number; name: string };
const users: User[] = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
<Table
data={users}
renderRow={(user) => (<>
<td>{user.id}</td>
<td>{user.name}</td>
</>)}
/>;
为什么有用
泛型使您的组件具有适应性,而不会失去类型推理的优势,从而使您免于重复的代码。
Mapped Types: props 和 state 之间的转换
映射类型允许您通过转换现有类型来创建新类型。这在定义派生 state 或 prop 接口时特别有用。
示例, 部分表单prpos
ts
type FormValues = {
name: string;
email: string;
age: number;
};
type FormErrors<T> = {
[K in keyof T]?: string;
};
const errors: FormErrors<FormValues> = {
name: "Name is required",
email: "Email is invalid",
};
为什么有用
映射类型非常适合管理具有不同属性的表单、API 或配置对象。
React props 和 state 的高级类型
带有 defaultProps
的类型属性
使用 defaultProps
时,您可以通过利用 TypeScript 的部分类型支持来确保类型安全
ini
type ButtonProps = {
label: string;
onClick?: () => void;
};
const Button = ({ label, onClick = () => {} }: ButtonProps) => (
<button onClick={onClick}>{label}</button>
);
只读 State
要在您的状态中强制执行不可变性,请使用 Readonly
或 ReadonlyArray
ts
type State = Readonly<{
items: string[];
}>;
const [state, setState] = useState<State>({ items: ['Item 1'] });
为什么有用
强类型的 props 和 state 减少了运行时错误,并使其他开发人员清楚地了解了你的意图。
使用 TypeScript 和 React 的动态表单
动态表单通常涉及依赖于用户输入的复杂类型。TypeScript 可以帮助强制执行正确性,即使对于动态结构也是如此。
动态表单字段
ts
type Field = {
id: string;
label: string;
value: string;
};
type FormState = {
fields: Field[];
};
const DynamicForm: React.FC = () => {
const [formState, setFormState] = useState<FormState>({ fields: [] });
const addField = () => {
setFormState((prev) => ({
fields: [...prev.fields, { id: Date.now().toString(), label: '', value: '' }],
}));
};
return (
<div>
{formState.fields.map((field) => (
<input
key={field.id}
placeholder={field.label}
value={field.value}
onChange={(e) =>
setFormState((prev) => ({
fields: prev.fields.map((f) =>
f.id === field.id ? { ...f, value: e.target.value } : f
),
}))}
/>
))}
<button onClick={addField}>Add Field</button>
</div>
);
};
为什么有用
动态表单是许多应用程序中的常见功能。TypeScript 确保每个字段都正确键入和更新。
总结
TypeScript 不仅仅是一个静态类型工具------它还是一种编写更健壮、可维护和可扩展的 React 应用程序的方法。通过掌握 discriminated unions(可区分联合)、泛型和高级 prop 类型等模式,您可以在项目中释放 TypeScript 的全部潜力。无论您是构建动态表单、可重用组件还是管理复杂状态,这些模式都能确保流畅高效的开发体验。
[原文地址](TypeScript Patterns You Should Know for React Development | by Dzmitry Ihnatovich | Medium)
广州有前端坑位滴滴我。