如何避免Formik在props变更时触发validate及深度相等校验?
避免Formik因Props变更触发Validate并取消深度相等检查的方案
你的问题核心是误用了validate的职责,且enableReinitialize的触发逻辑未做优化,导致需要靠深度相等来避免不必要的状态更新。完全可以不用深度相等,以下是具体解决方案:
1. 把状态更新从validate中剥离
validate的设计目的是返回表单验证错误,不是用来处理状态更新的。你现在在validate里调用setData,会导致每次validate执行(包括props变更触发的重新初始化)都走一遍对比逻辑,这是不合理的。
2. 用onValuesChange替代validate做状态更新
Formik的onValuesChange只会在用户主动修改表单值时触发,props变更导致的重新初始化不会触发这个方法。结合局部字段对比(而非全量深度对比)就能避免不必要的setData调用:
修改后的代码示例
import { useMemo, useRef } from 'react'; // 自定义简易浅对比函数,针对数组和对象做基础对比 const shallowCompareArrays = (a: any[], b: any[]) => { if (a.length !== b.length) return false; return a.every((item, idx) => JSON.stringify(item) === JSON.stringify(b[idx])); }; const ColumnsCustomizer = ({ data, setData }: { data: DataTableType | null; setData: (value: DataTableType | null) => void; }) => { const deleteColumn = (index: number) => { // 你的逻辑... }; const addColumn = (index: number) => { // 你的逻辑... }; // 缓存initialValues,仅当依赖的核心字段变化时才更新,减少enableReinitialize的触发 const initialValues = useMemo(() => ({ headers: data.headers, maxIndex: data.headers.length, properties: data.properties }), [data.headers, data.properties]); // 存储上一次的有效表单值,避免重复更新状态 const prevFormValues = useRef(initialValues); return ( <SidebarSection title=""> {data && data.rows ? ( <Formik enableReinitialize={true} initialValues={initialValues} onSubmit={(values, { setSubmitting }) => { setData({ ...data, headers: values.headers, properties: values.properties }); setSubmitting(false); }} validateOnChange={true} validateOnBlur={false} // 仅保留真正的验证逻辑 validate={(values) => { const errors = {}; // 示例:验证表头不能为空 if (!values.headers?.length) { errors.headers = "至少需要保留一个表头"; } // 其他自定义验证规则... return errors; }} // 仅在用户修改表单时触发状态更新 onValuesChange={(values) => { // 只对比需要同步的核心字段:headers和properties const headersChanged = !shallowCompareArrays(values.headers, prevFormValues.current.headers); const propertiesChanged = JSON.stringify(values.properties) !== JSON.stringify(prevFormValues.current.properties); if (headersChanged || propertiesChanged) { setData({ ...data, headers: values.headers, properties: values.properties }); prevFormValues.current = values; } }} > {({ values }) => ( <Form> {/* 你的表单组件内容... */} </Form> )} </Formik> ) : null} </SidebarSection> ); };
3. 额外优化:减少enableReinitialize的触发次数
通过useMemo缓存initialValues,只依赖data.headers和data.properties,这样只有当这两个核心字段真正变化时,Formik才会重新初始化,避免因data其他无关字段变更导致的不必要validate调用。
为什么不用深度相等?
JSON.stringify性能差,数据量大时会带来明显的性能开销;JSON.stringify不可靠,对象属性顺序变化、特殊值(如undefined、NaN)会导致对比结果错误;- 你只需要同步
headers和properties的变化,maxIndex是从headers.length派生的,不需要参与对比,局部对比更高效精准。
内容的提问来源于stack exchange,提问作者Oleg Ivsv
相关产品推荐
相关产品推荐

