React Hook Form问题:onChange模式仍触发失焦验证且影响提交
问题分析与解决方案
核心问题原因
- 失焦时重复验证:React Hook Form 默认会在字段失焦时触发验证(
shouldValidateOnBlur默认值为true),即使你设置了mode: 'onChange',这个行为依然存在,导致 onChange 验证后,失焦时又会重新触发一次验证。 - 首次提交无响应:点击提交按钮时,输入框会失焦触发 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
相关产品推荐
相关产品推荐

