React-Hook-Form无法读取状态对象字段问题求助
问题描述
我使用react-select异步组件向API发起请求,希望将返回对象的各字段显示在React-Hook-Form(RHF)的输入框中。目前遇到的问题是:这些字段注册后能成功写入表单状态,但完全不响应状态变化——无论是通过异步组件选择选项,还是显式调用setValue设置表单状态,输入框都不会显示对应的值。
我尝试了两种绑定方式:直接用register绑定input,以及用Controller包裹input,但两种方式都无法让输入框响应表单状态更新。代码如下:
const formMethods = useForm<FormFields>({ defaultValues: { todo: { title: "", id: "", }, }, }); return ( <div className="row"> <Controller control={formMethods.control} name="todo" render={(params) => ( <AsyncCreatableSelect defaultOptions getOptionLabel={(todo) => todo.title} getOptionValue={(todo) => todo.id} loadOptions={promiseOptions} {...params.field} /> )} /> <div> <Controller control={formMethods.control} name="todo.id" render={(params) => { return ( <label> Controller-bound <input {...params.field} value={params.field.value} /> </label> ); }} /> </div> <label> Register-bound <input {...formMethods.register("todo.title")} /> </label> <div> <button onClick={() => { formMethods.setValue( "todo", { userId: 1, id: 1, title: "delectus aut autem", completed: false, }, { shouldDirty: true, shouldTouch: true } ); }} > Set Object Value </button> </div> </div>
解决方案
1. 统一类型与默认值结构
首先确保FormFields的类型定义和API返回的Todo结构完全匹配,同时让默认值的结构和类型与更新对象一致,避免类型不兼容导致的状态同步异常:
// 定义完整的Todo类型 type Todo = { userId: number; id: number; title: string; completed: boolean; }; type FormFields = { todo: Todo; }; // 初始化表单时使用匹配的默认值 const formMethods = useForm<FormFields>({ defaultValues: { todo: { userId: 0, id: 0, title: "", completed: false, }, }, });
2. 修正react-select的getOptionValue
react-select要求getOptionValue返回字符串类型,若API返回的id是数字,需转为字符串:
<AsyncCreatableSelect defaultOptions getOptionLabel={(todo) => todo.title} // 将数字id转为字符串 getOptionValue={(todo) => todo.id.toString()} loadOptions={promiseOptions} {...params.field} />
3. 优化输入框的绑定逻辑
- 对于
Controller绑定的输入框,无需手动指定value={params.field.value},直接展开{...params.field}即可,Controller已自动处理值的同步:
<Controller control={formMethods.control} name="todo.id" render={(params) => ( <label> Controller-bound <input {...params.field} /> </label> )} />
- 直接使用
register绑定的输入框无需额外调整,确保字段路径todo.title与表单状态结构匹配即可。
修正后的完整关键代码
type Todo = { userId: number; id: number; title: string; completed: boolean; }; type FormFields = { todo: Todo; }; export default function App() { const formMethods = useForm<FormFields>({ defaultValues: { todo: { userId: 0, id: 0, title: "", completed: false, }, }, }); const promiseOptions = async (inputValue: string) => { const res = await fetch(`https://jsonplaceholder.typicode.com/todos?q=${inputValue}`); const todos = await res.json(); return { options: todos }; }; return ( <div className="row"> <Controller control={formMethods.control} name="todo" render={(params) => ( <AsyncCreatableSelect defaultOptions getOptionLabel={(todo) => todo.title} getOptionValue={(todo) => todo.id.toString()} loadOptions={promiseOptions} {...params.field} /> )} /> <div> <Controller control={formMethods.control} name="todo.id" render={(params) => ( <label> Controller-bound <input {...params.field} /> </label> )} /> </div> <label> Register-bound <input {...formMethods.register("todo.title")} /> </label> <div> <button onClick={() => { formMethods.setValue( "todo", { userId: 1, id: 1, title: "delectus aut autem", completed: false, }, { shouldDirty: true, shouldTouch: true } ); }} > Set Object Value </button> </div> </div> ); }
问题根源总结
- 类型不匹配:初始默认值的
todo.id为字符串,而setValue传入的是数字,导致RHF无法正确同步值到输入框; - react-select配置错误:
getOptionValue返回数字而非字符串,导致选中选项后存储的todo.id类型不一致; - 默认值结构缺失字段:初始默认值缺少
userId和completed字段,更新完整对象时RHF状态同步异常。
内容的提问来源于stack exchange,提问作者ABMagil
相关产品推荐
相关产品推荐

