React.FunctionComponent与普通函数定义组件的差异及用法疑问
我在TypeScript里通常这么定义React函数组件:
// 定义Options组件和viewOptions属性 type OptionsProps = { viewOptions: ViewOptions; }; export const Options: React.FunctionComponent<OptionsProps> = (props: OptionsProps) => { const {viewOptions} = props; // 这里写使用viewOptions渲染元素的代码 }
现在想问问,能不能统一改成下面这种方式定义?
// 用简单箭头函数实现同样功能 export const Options = (viewOptions: ViewOptions): JSX.Element => { // 这里写使用viewOptions渲染元素的代码 }
我猜测两者只有调用方式不同:
<Options viewOptions={viewOptions}/>-- 作为React元素调用{Options(viewOptions)}-- 作为普通函数调用
除此之外,二者还有其他差异吗?
- 可用性方面?后者看起来也能调用Hook,算是React函数组件吧?
- 行为方面?比如参数变化时的重渲染逻辑有没有区别?
核心差异分析
1. 类型系统层面的区别
第一种写法通过React.FunctionComponent<OptionsProps>(可简写为React.FC<OptionsProps>)明确标注了组件类型,React会自动为组件注入**children属性的默认类型**(即使你没在Props里定义);如果不需要children,你可以显式使用PropsWithChildren<OptionsProps>或者禁用这个默认行为。
而第二种写法直接定义函数参数类型,不会自动添加children的类型支持——如果你的组件需要接收children,必须手动在参数里声明,否则TypeScript会报错。
另外,用React.FC定义的组件会被TypeScript识别为标准React组件类型,在组件库类型约束、高阶组件参数等需要组件类型的场景下兼容性更好;第二种写法的函数类型是普通的(viewOptions: ViewOptions) => JSX.Element,虽能当作组件用,但在严格类型检查场景下可能需要额外的类型断言。
2. 组件身份识别与调试
React内部对React.FC定义的组件有更明确的标识,在DevTools调试时,组件名称会更清晰(尤其是配合TS的类型推断)。第二种普通箭头函数写法,若没显式命名函数,DevTools可能会显示为Anonymous,不过你这里已经给了函数名Options,所以差异不大,但部分工具链或第三方库可能依赖React.FC的类型标识做优化或校验。
3. 重渲染逻辑
两者的重渲染逻辑没有本质区别:当作为React元素(<Options .../>)调用时,React都会遵循函数组件的重渲染规则——只有当组件的props(第一种的props对象、第二种的viewOptions参数)发生浅比较变化时,才会触发重渲染。
如果作为普通函数调用({Options(viewOptions)}),它就只是普通函数执行,不会纳入React组件生命周期管理,每次父组件重渲染时这个函数都会被执行一次,相当于直接渲染JSX片段,无法使用React的重渲染优化(比如memo)。
4. Hook可用性
两种写法都可以正常调用React Hook,只要符合Hook的规则(只在函数组件顶层调用、不能在条件语句里调用等),这一点上可用性完全一致。
总结
如果你的组件不需要自动的children类型支持,且不依赖React.FC的类型标识场景,第二种写法完全可以用。但如果需要更好的类型兼容性、自动的children类型支持,或者团队有统一代码规范,第一种写法更稳妥。
内容的提问来源于stack exchange,提问作者ChrisW

