React Hook Form中useFormContext的watch监听嵌套表单结构未触发组件重渲染问题
React Hook Form中useFormContext的watch监听嵌套表单结构未触发组件重渲染问题
嘿,我来帮你排查下这个React Hook Form的监听问题~
首先,你的核心问题出在watch的监听方式和表单字段路径的写法上,另外TypeScript里的as never强制转换也确实是个信号,说明路径写法不符合React Hook Form的规范,咱们一步步来解决:
1. 问题根源:错误的字段路径与监听逻辑
你在Controller的name里写了fees.[fuel_costs][0].quantity,还有watch里先监听整个fees再链式取值的方式都有问题:
- React Hook Form的字段路径语法中,对象的键不需要用方括号包裹(动态键场景更推荐用数组形式路径),正确的路径应该是
fees.fuel_costs[0].quantity或者数组形式['fees', 'fuel_costs', 0, 'quantity']。 - 直接
watch('fees')只会在fees对象的引用发生变化时触发重渲染,而嵌套字段(比如fuel_costs[0].quantity)的更新并不会改变fees的引用,所以组件不会重新渲染。
2. 修正后的代码示例
调整watch的监听逻辑
直接监听目标嵌套路径,用数组形式的路径更利于TypeScript类型推断,也更可靠:
const { watch, control } = useFormContext<QuoteCalculatorValues>(); // 数组形式监听精准路径 const fuelCostWatch = watch(['fees', 'fuel_costs', 0]); // 或者字符串形式 // const fuelCostWatch = watch('fees.fuel_costs[0]');
修正Controller的配置
另外你在Input的onChange里手动调用setValue是多余的——Controller的field已经包含了正确的 onChange 逻辑,直接传给Input即可,同时修正name的写法:
<Controller name={['fees', 'fuel_costs', 0, 'quantity']} // 数组路径让类型推断更准确 control={control} render={({ field }) => ( <Input field={field} defaultValue={0} // 去掉多余的手动setValue,field.onChange已经会处理值更新 /> )} />
这样修改后,当你修改Input的值时,watch监听的fees.fuel_costs[0]会检测到变化并触发组件重渲染,同时TypeScript的类型错误也会消失,不需要再用as never强制转换了。
3. 为什么监听整个表单有效?
当你用watch().fees['fuel_costs']?.[0]时,watch()没有传参数,会监听所有表单字段的变化,只要任何字段更新都会触发重渲染,所以能看到效果。但这种方式在表单数据量大时确实会有性能隐患,而精准监听特定路径就能避免这个问题。
备注:内容来源于stack exchange,提问作者Francis Ducharme
相关产品推荐
相关产品推荐

