如何实现react-hook-form Controller与自定义state变量绑定并同步更新值?
问题根源
你当前的写法存在状态流断裂,本质是同时维护了两套互不同步的状态:
- react-hook-form 的
Controller本身有独立的内部表单状态,你给它传的value={addBill.debitAmt}、defaultValue={addBill.debitAmt}并没有打通到Controller的状态更新逻辑里 - NumberFormat的
onValueChange回调里只更新了外部的addBill状态,从来没有把更新后的值同步回Controller的内部状态 - 你还混用了已被废弃的
getInputRef+ 原生register的写法,这在新版Controller的API体系里完全不生效
所以当你手动调用setAddBill改值时,Controller渲染时取的还是自己内部存的旧value,绑定在它内部的NumberFormat自然不会展示新值。
正确实现步骤
1. 初始化useForm时打通初始值
首先从useForm里拿到需要的API,把addBill里的初始值作为表单的默认值传入:
const { control, setValue, formState: { errors } } = useForm({ defaultValues: { debitAmt: addBill.debitAmt // 把外部state的初始值作为表单初始值 } })
2. 重写Controller的渲染逻辑
替换原来错误的Controller属性,通过render函数拿到的field对象来双向绑定值,不要再手动传value、getInputRef这类冗余属性:
<FormGroup className="mr-10 mb-10"> <Label for="debitAmt" className="mr-sm-10">Debit Amt</Label> <Controller name="debitAmt" control={control} // 校验规则直接写在Controller上,替代原来的getInputRef里的register rules={{ required: true }} className="form-control" style={setErrorStyle(errors.debitAmt)} aria-invalid={errors.debitAmt ? "true" : "false"} render={({ field }) => ( <NumberFormat value={field.value} thousandSeparator={true} prefix="$" onValueChange={(v) => { const inputVal = v.floatValue === undefined ? '' : v.floatValue // 第一步:把值同步回react-hook-form的内部状态 field.onChange(inputVal) // 第二步:按需求同步更新外部的addBill state // 用函数式更新避免拿到过期的闭包state setAddBill(prev => ({...prev, debitAmt: inputVal})) }} /> )} /> {errors.debitAmt && ( <span style={{ color: "red" }} role="alert">required</span> )} </FormGroup>
3. 补全外部state变化的同步逻辑
因为Controller只会响应自己内部的状态变化,当你手动调用setAddBill修改debitAmt值时,需要额外把新值同步回表单内部:
useEffect(() => { // 监听addBill.debitAmt的变化,同步更新对应表单项的值 setValue('debitAmt', addBill.debitAmt) }, [addBill.debitAmt, setValue])
完成这一步后,当你执行setAddBill({...addBill, debitAmt: 10})时,Effect会监听到值变化,通过setValue把10写入Controller的内部状态,NumberFormat就能拿到最新值正常渲染了。
常见避坑提醒
- 不要直接给Controller传外部state作为
valueprop,这会打乱Controller的内部状态更新逻辑,所有绑定的展示值统一从render函数返回的field.value里取 - 如果业务场景不需要单独维护
addBill这个外部state,可以完全删掉这个useState,所有表单值的读写直接通过react-hook-form的watch、setValue、getValuesAPI完成,能减少大量双向同步的代码 - 不要混用原生register和Controller的写法,两者的状态存储是独立的,混用会导致校验失效、值不同步的问题
内容的提问来源于stack exchange,提问作者myTest532 myTest532
相关产品推荐
相关产品推荐

