You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 12:42:52