在 React + TypeScript 项目中,React.FC(即 React.FunctionComponent 的别名)曾经是定义函数组件的常见方式,但如今社区普遍推荐避免使用它,转而直接注解组件的 props 类型。下面我一步步解释原因和演变过程。
历史背景和早期问题
早期(React 17 及更早版本结合旧 TypeScript 时),React.FC 被广泛使用,因为它提供了一种简便的类型注解方式,能自动推断一些静态属性如 displayName、propTypes 和 defaultProps。然而,它存在几个显著缺点:
- 隐式 children prop:
React.FC会自动将children添加到 props 类型中,即使组件不需要它。这会导致类型不准确,例如如果你定义一个不接受 children 的组件,用户仍能传入 children 而不会报错。 - 泛型支持不佳:使用
React.FC时,泛型组件的类型定义变得复杂且不易读。 - defaultProps 问题:与
defaultProps结合时,可能出现类型不兼容或推断错误。 - 返回类型限制:
React.FC强制返回React.ReactElement,不允许直接返回null、string、number或其他类型,这在某些场景下不灵活。 - 函数类型应用困难:
React.FC本质上是函数类型,难以直接应用于命名函数,导致代码更冗长。
这些问题导致社区(如 Create React App 在 2020 年移除对 React.FC 的默认使用)开始转向其他方式。 许多知名开发者、TypeScript 贡献者和框架(如 Backstage)也公开不推荐它。
最近变化(React 18 和 TypeScript 5.1 后)
从 React 18(2022 年发布)开始,React.FC 的许多问题被修复:
- 移除了隐式
childrenprop,现在只有显式定义时才会存在。 - 支持更多返回类型,不再严格限制为
React.ReactElement。
TypeScript 5.1(2023 年)进一步优化了这些,使得 React.FC 在技术上“可用”且不再“危险”。如果你已经在代码中使用它,也没有必要急于移除。
为什么现在还是不推荐使用?
尽管修复了早期缺陷,社区共识(包括 2025 年的最新讨论)仍建议避免 React.FC,因为它不再是最佳实践。理由包括:
- 更简单的语法和更少 boilerplate:直接注解 props 更简洁、直观,符合纯 TypeScript 风格,而
React.FC引入了不必要的框架特定类型。 - 更好的灵活性:支持泛型、复合组件和“组件作为命名空间”的模式,而不需额外处理。
- 学习曲线和可读性:新手可能觉得
React.FC容易上手,但长期看,直接类型定义更易维护和阅读。 - 现代工具支持:像 ESLint 规则和 codemod 可以自动禁止或替换
React.FC。 - 无额外价值:
React.FC提供的自动推断(如静态属性)在现代 TypeScript 中已无需它就能实现。
总体上,使用 React.FC 不会出错,但它被视为过时,类似于一些旧的 React 模式。 如果你的项目是新启动的,或在重构时,建议切换到更现代的写法。
推荐的替代方式
直接定义函数组件,并注解 props 类型。示例:
// 旧方式(不推荐)
const MyComponent: React.FC<{ name: string }> = ({ name }) => <div>Hello, {name}</div>;
// 新方式(推荐)
function MyComponent({ name }: { name: string }) {
return <div>Hello, {name}</div>;
}
// 或箭头函数
const MyComponent = ({ name }: { name: string }) => <div>Hello, {name}</div>;
// 如果需要 children 或其他复杂类型
type Props = {
name: string;
children?: React.ReactNode; // 显式添加,如果需要
};
function MyComponent({ name, children }: Props) {
return <div>{name}{children}</div>;
}
这种方式更贴近 JavaScript 原生语法,同时充分利用 TypeScript 的类型推断。