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

Formik setFieldTouched同步校验为何能获取最新state值

请问该校验难道不是同步执行的吗?

查看Formik.tsx文件中的相关提交记录可以发现,校验逻辑最初(以touched状态触发校验的场景为例)是写在useEffect中的:

React.useEffect(() => {
    if (
      prevState.touched !== state.touched &&
      !!validateOnBlur &&
      !state.isSubmitting &&
      isMounted.current != null
    ) {
      const [validate, cancel] = makeCancelable(validateForm());
      validate.then(x => x).catch(x => x); // 静默捕获Promise拒绝结果
      return cancel;
    }
    return;
  }, [
    prevState.touched,
    state.isSubmitting,
    state.touched,
    validateForm,
    validateOnBlur,
    isMounted,
  ]);

后续该逻辑被迁移到了setFieldTouched的React.useCallback实现中:

const setFieldTouched = React.useCallback(
    (
      field: string,
      touched: boolean = true,
      shouldValidate: boolean = true
    ) => {
      dispatch({
        type: 'SET_FIELD_TOUCHED',
        payload: {
          field,
          value: touched,
        },
      });
      return validateOnBlur && shouldValidate
        ? validateFormWithLowPriority(state.values)
        : Promise.resolve();
    },
    [validateFormWithLowPriority, state.values, validateOnBlur]
  );

再看当前的最新实现,setFieldTouched是通过useEventCallback包裹的:

const setFieldTouched = useEventCallback(
    (field: string, touched: boolean = true, shouldValidate?: boolean) => {
      dispatch({
        type: 'SET_FIELD_TOUCHED',
        payload: {
          field,
          value: touched,
        },
      });
      const willValidate =
        shouldValidate === undefined ? validateOnBlur : shouldValidate;
      return willValidate
        ? validateFormWithHighPriority(state.values)
        : Promise.resolve();
    }
  );

但后两种实现为什么、又是如何保证校验运行时能使用「最新的」表单值的呢?

在最初useEffect的实现逻辑中,开发者可能先dispatch值变更动作,再dispatch失焦动作,两个动作都会修改state,等state完成更新后会触发effect执行,此时effect中自然能拿到最新的当前值。
但在后两种实现中,顺着调用链路追踪,校验本身应该是同步运行的(已知结论:Promise的executor函数是同步执行的),只有错误状态的设置会等待Promise完成后才执行(存在显式的then调用)。按这个逻辑推断,当我调用setFieldValue后立刻调用setFieldTouched时,由于React的state更新是异步的,此时拿到的应该还是旧的state值——就像我们dispatch一个state变更动作后,立刻读取同一个state,拿到的仍然是旧值一样(可参考React state异步更新的相关示例)。
但实际测试Formik运行逻辑时会发现,setFieldTouched校验流程中传入validate函数的values确实是最新的实际值。我无法理解出现该现象的原因,可能是我对执行流程的理解存在偏差,因此提出该问题。


内容的提问来源于stack exchange,提问作者Dean

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 15:57:52