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

React Hook Form问题:onChange模式仍触发失焦验证且影响提交

问题分析与解决方案

核心问题原因

  1. 失焦时重复验证:React Hook Form 默认会在字段失焦时触发验证(shouldValidateOnBlur 默认值为 true),即使你设置了 mode: 'onChange',这个行为依然存在,导致 onChange 验证后,失焦时又会重新触发一次验证。
  2. 首次提交无响应:点击提交按钮时,输入框会失焦触发 onBlur 验证,而你的异步验证带有 500ms 防抖,此时表单处于 isValidating 状态,提交按钮因 disabled 条件被拦截,无法执行提交逻辑。等防抖验证完成后,按钮恢复可用,第二次点击就能正常提交。

解决方案

1. 禁用失焦验证

修改 useForm 配置,添加 shouldValidateOnBlur: false,彻底阻止失焦时的验证触发:

const form = useForm<OnboardingFormValues>({
  resolver: zodResolver(onboardingFormSchema),
  defaultValues: {
    username: '',
  },
  mode: 'onChange',
  shouldValidateOnBlur: false, // 禁用失焦验证
})

2. 优化防抖验证函数(避免竞态条件)

原防抖逻辑存在潜在的竞态问题(旧的验证请求可能覆盖最新结果),优化后确保只有最新的验证请求生效:

const createDebouncedValidator = () => {
  let latestRequestId = 0;
  const debouncedCheck = debounce(async (username: string, requestId: number) => {
    try {
      const { available } = await checkAvailability('name', username);
      // 仅当当前请求是最新的才返回结果
      if (requestId === latestRequestId) {
        return available;
      }
    } catch {
      if (requestId === latestRequestId) {
        return false;
      }
    }
    return false;
  }, 500);

  return async (username: string) => {
    if (username.length < 3) return true;

    latestRequestId++;
    const currentRequestId = latestRequestId;
    return debouncedCheck(username, currentRequestId);
  };
};

3. 可选:调整提交按钮的禁用逻辑

原按钮禁用条件依赖 formState.isValid,而异步验证完成前 isValid 为 false,可以考虑仅在 isValidating 或 isSubmitting 时禁用按钮,让 handleSubmit 来处理最终验证:

<Button 
  type='submit' 
  className='w-full'
  disabled={form.formState.isValidating || form.formState.isSubmitting}
>
  {form.formState.isSubmitting ? t('common.saving') : 'Continue'}
</Button>

这样用户输入符合基础规则(长度、格式)后,按钮就会提前可用,点击提交时会等待异步验证完成后再执行提交逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 17:17:03