Formik 2.2.9版本中FastField未按预期减少重渲染问题咨询
问题解答
关于FastField的理解是否正确
你的理解部分正确,但存在遗漏:FastField的重渲染判定逻辑是仅监听自身关联字段的value、error、touched三个属性的变化,默认不会响应Formik全局上下文的其他状态变更(比如其他字段值变化、全局isSubmitting状态变化等)。但有两个例外会触发FastField的全量重渲染:
- 你给FastField传递的props是每次渲染都重新生成的引用类型(比如行内定义的onChange、className对象、未memo化的组件作为children)
- Formik的
enableReinitialize属性设为true,且顶层initialValues每次渲染都生成新的引用
Formik 2.2.9版本FastField的已知问题
这个版本的FastField确实存在已知的重渲染缺陷:
- 当表单任意字段触发touched状态变更时(就是你遇到的focus/blur场景),Formik的顶层state会更新,FastField内部的浅层对比逻辑会误判上下文变化,导致所有FastField都触发重渲染,这个问题在Formik 2.x的多个小版本中都存在,至今未完全修复。
- 动态生成的FastField如果没有设置稳定的
key属性,也会被React判定为新组件,重复挂载重渲染。
可行的解决方案
- 给DatePicker组件套memo,自定义对比逻辑
把你封装的DatePicker输入组件用React.memo包裹,自定义对比函数只判断当前字段对应的value、touched、error三个属性是否变化,忽略其他props:
后续FastField内部直接调用这个memo后的组件即可。const MemoizedDatePicker = React.memo(({ field, form, ...props }) => { return <DatePicker {...field} {...props} /> }, (prevProps, nextProps) => { return prevProps.field.value === nextProps.field.value && prevProps.form.touched[prevProps.field.name] === nextProps.form.touched[nextProps.field.name] && prevProps.form.errors[prevProps.field.name] === nextProps.form.errors[nextProps.field.name] }) - 稳定所有props的引用
所有传递给FastField/输入组件的回调函数、配置对象都用useCallback/useMemo包裹,不要在行内直接定义。动态生成的字段设置稳定唯一的key(不要用数组索引,用业务字段的唯一标识)。 - 放弃FastField,改用字段隔离的状态管理方案
64个字段的场景如果对性能要求极高,可以用useFormikContext只取当前字段需要的三个属性,配合memo实现完全的渲染隔离,完全绕开FastField的缺陷。 - 升级到Formik v3 beta版本
v3版本重构了状态订阅逻辑,每个字段只订阅自身需要的状态变更,从根源上解决了全局状态更新导致全量重渲染的问题,只是beta版本可能存在其他兼容性问题,需要结合业务场景评估。
内容的提问来源于stack exchange,提问作者Stewart Alan
相关产品推荐
相关产品推荐

