React Hook Form Controller组件挂载时自动更新值的实现问题
问题解答
核心结论:不能在render方法/函数组件渲染逻辑内设置值
React要求组件的渲染阶段是纯函数,不能包含副作用(比如调用onChange修改外部状态)。如果在render里直接调用onChange,会触发状态更新,进而导致组件重新渲染,再次执行render逻辑,形成无限重渲染循环,这是React严格禁止的行为。
原问题原因分析
你在TestInput的useEffect中调用onChange触发field.onChange,理论上会更新React Hook Form的表单状态,Controller应该感知到状态变化,重新传递更新后的field.value给TestInput,从而触发组件重新渲染。如果没生效,可能的原因包括:
- 表单初始值与你设置的
'AutoSet'相同,状态未发生变化,所以没有触发重渲染 useEffect的执行时机问题(挂载时执行,但此时Controller的表单状态还未完成初始化)
正确解决方案
方案1:修正组件内部逻辑,确保状态更新能触发重渲染
保证TestInput的副作用依赖项正确,同时让onChange能正确传递到父组件更新表单状态:
type TProps = { value: string; onChange?: (value: string) => void; }; const TestInput = ({ onChange, value }: TProps) => { // 模拟组件内部的数据请求逻辑 useEffect(() => { const fetchData = async () => { // 替换为实际的异步请求 const result = await Promise.resolve('AutoSet'); onChange?.(result); }; fetchData(); }, [onChange]); // 添加onChange到依赖项,避免闭包问题 return <div>{value || 'empty'}</div>; }; type Props = { name: string; control: Control<FieldValues>; }; const TestInputController = ({ name, control }: Props) => { return ( <Controller control={control} render={({ field }) => { return ( <TestInput value={field.value} onChange={(value) => { field.onChange(value); }} /> ); }} name={name} defaultValue="" // 设置初始值为空,确保状态变化能被感知 /> ); };
方案2:直接使用React Hook Form的setValue操作表单状态
如果数据请求逻辑可以放在TestInputController中,直接用setValue更符合React Hook Form的使用规范:
const TestInputController = ({ name, control }: Props) => { const { setValue } = control; useEffect(() => { const fetchData = async () => { const result = await Promise.resolve('AutoSet'); setValue(name, result); }; fetchData(); }, [name, setValue]); return ( <Controller control={control} render={({ field }) => { return <TestInput value={field.value} onChange={field.onChange} />; }} name={name} defaultValue="" /> ); };
内容的提问来源于stack exchange,提问作者Sergey Yu
相关产品推荐
相关产品推荐

