React Hook Form注销必填字段后如何让formState.isValid立即为true
问题根因
这是React Hook Form的机制特性导致的状态不同步:
unregister方法默认异步更新校验状态,加上formState本身是Proxy代理对象,如果你没有显式订阅isValid属性,组件不会在状态更新后自动重渲染,自然拿不到最新的isValid=true结果- 加
console.log(formState.isValid)后功能正常,本质是console读取属性的操作触发了Proxy的依赖收集,让组件订阅到了isValid的更新,移除打印逻辑后依赖收集失效,状态更新不会触发组件重渲染 - 页面初始加载默认选中e-Money时,ePIN、eNumber两个字段还没完成挂载注册,切到cash时调用unregister相当于注销不存在的字段,不会触发校验重算,isValid就一直卡在false;先手动切到e-Money让两个字段完成注册,再切回cash时注销已存在的字段才会触发校验更新,这就是“必须先切一次电子支付再切回货到付款才正常”的原因。
修复方案
方案1(最小改动):给unregister加配置项,强制注销后立刻重校验
unregister支持第二个配置参数,传入shouldValidate: true就会在注销字段后立刻同步触发表单重校验,更新isValid状态,只需要修改useEffect里的逻辑即可:
useEffect(() => { if (method === "cash") { // 增加shouldValidate配置,注销字段后立刻重算表单有效性 unregister("ePIN", { shouldValidate: true }); unregister("eNumber", { shouldValidate: true }); } },[method, unregister])
改完后不需要额外加console打印,切到货到付款选项时,两个电子支付字段注销后会立刻重算校验状态,其余字段填写正确的话isValid会直接更新为true。
方案2(推荐最佳实践):放弃手动unregister,用动态校验规则
更符合React Hook Form设计逻辑的写法是不手动控制字段注册/注销,直接根据当前选中的支付方式动态给电子支付字段加必填规则,字段隐藏时自动不触发必填校验,完全不需要写useEffect调用unregister:
// e-Money Number字段动态规则 {...register("eNumber", { required: method === "e-money", // 仅选中电子支付时才校验必填 min: 9, maxLength: 9 })} // e-Money PIN字段动态规则 {...register("ePIN", { required: method === "e-money", // 仅选中电子支付时才校验必填 min: 4, maxLength: 4 })}
这种写法下RHF会自动根据规则变化更新isValid状态,从根源上避免手动调用unregister带来的状态不同步问题。
额外优化建议
你当前的支付按钮是绑定自定义onClick事件手动判断isValid,更稳妥的写法是把按钮设为表单原生提交按钮,直接利用handleSubmit的内置校验逻辑:校验通过才会执行onSubmit回调,在回调里打开支付弹窗即可,完全不需要手动判断formState.isValid,能规避绝大多数状态同步问题:
// 提交逻辑直接放在onSubmit里 const onSubmit = data => { console.log(data); setPayModal(true); } // 按钮改为submit类型,放在form标签内部,移除自定义onClick <Button className="pay-btn" text="CONTINUE & PAY" type="type-1" // 注意要给组件透传原生button的type="submit"属性 />
内容的提问来源于stack exchange,提问作者envincebal
相关产品推荐
相关产品推荐

