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

如何实现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作为value prop,这会打乱Controller的内部状态更新逻辑,所有绑定的展示值统一从render函数返回的field.value里取
  • 如果业务场景不需要单独维护addBill这个外部state,可以完全删掉这个useState,所有表单值的读写直接通过react-hook-form的watch、setValue、getValues API完成,能减少大量双向同步的代码
  • 不要混用原生register和Controller的写法,两者的状态存储是独立的,混用会导致校验失效、值不同步的问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.24 06:15:09