React-Hook-Form无法获取Select赋值的Input值问题解决
React-Hook-Form下拉框同步值到输入框后提交为空的解决方案
问题描述
使用React-Hook-Form开发表单时,设置了Select下拉框,选中选项后将值同步到名为customers的输入框,但提交表单时该输入框的值始终为空。
原代码示例
import { useState } from "react"; import { useForm } from "react-hook-form"; function App() { const [inputs, setInputs] = useState(); const [inputs1, setInputs1] = useState(); const { register, formState: { errors }, trigger, handleSubmit } = useForm({ defaultValues: { searchby: "searchby", customers: "", firstName: "" } }); const onSubmit = (data) => { alert(JSON.stringify(data)); }; const handleInputChanges = (event) => { const name = event.target.name; const value = event.target.value; setInputs(value); setInputs1(value); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <select name="searchby" {...register("searchby", { required: "password is required." })} value={inputs} onChange={handleInputChanges} > <option selected value="searchby"> Search By </option> <option value="customerID">Custimer ID </option> <option value="teleco">Teleco</option> </select> {errors.searchby && <p>This field is Required</p>} <label>Customer: </label> <input name="customers" {...register("customers")} value={inputs1} onChange={handleInputChanges} /> {errors.customers && <p>This field is Required</p>} <label>First name: </label> <input {...register("firstName", { required: true })} /> {errors.firstName && <p>This field is Required</p>} <input type="submit" /> <button type="button" > Validate All </button> </form> ); }
问题原因
- 表单状态追踪失效:通过本地
useState维护inputs1并绑定到customers输入框的value,同时使用了React-Hook-Form的register,但自定义的onChange仅更新本地state,未通知React-Hook-Form更新表单数据,导致提交时RHF读取的还是customers字段的初始空值。 - 事件覆盖冲突:
register方法会自动生成onChange事件追踪值变化,但自定义的onChange覆盖了该事件,进一步导致RHF无法感知customers输入框的数值变化。 - 逻辑错误:
handleInputChanges会在下拉框和输入框变化时同时更新inputs和inputs1,导致输入框手动输入的内容被下拉框的值覆盖,逻辑混乱。
解决方案
方案一:使用setValue同步表单状态(轻量场景推荐)
通过React-Hook-Form提供的setValue方法,在下拉框变化时直接更新customers字段的表单值,让RHF能追踪到数值变化。
修正后代码:
import { useState } from "react"; import { useForm, useWatch } from "react-hook-form"; function App() { // 初始化下拉框默认值为defaultValues中的值 const [inputs, setInputs] = useState("searchby"); const { register, formState: { errors }, handleSubmit, setValue, // 新增:手动更新表单字段 watch, // 新增:监听表单字段实时值 trigger // 手动触发校验 } = useForm({ defaultValues: { searchby: "searchby", customers: "", firstName: "" } }); const onSubmit = (data) => { alert(JSON.stringify(data)); }; const handleInputChanges = (event) => { const name = event.target.name; const value = event.target.value; setInputs(value); // 仅当下拉框变化时,同步更新customers表单字段 if (name === "searchby") { setValue("customers", value); } }; // 监听customers字段的实时值,用于输入框显示 const customerValue = watch("customers"); return ( <form onSubmit={handleSubmit(onSubmit)}> <select name="searchby" {...register("searchby", { required: "This field is required." // 修正错误提示文案 })} value={inputs} onChange={handleInputChanges} > <option value="searchby">Search By</option> <option value="customerID">Customer ID</option> <option value="teleco">Teleco</option> </select> {errors.searchby && <p>{errors.searchby.message}</p>} <label>Customer: </label> <input name="customers" {...register("customers")} value={customerValue} // 使用watch获取的表单值,而非本地state /> {errors.customers && <p>This field is Required</p>} <label>First name: </label> <input {...register("firstName", { required: true })} /> {errors.firstName && <p>This field is Required</p>} <input type="submit" /> <button type="button" onClick={() => trigger()}> Validate All </button> </form> ); } export default App;
关键修改点:
- 从
useForm解构setValue和watch,用setValue更新表单字段,watch获取实时值 - 调整
handleInputChanges逻辑,仅在下拉框变化时同步customers值 - 输入框
value绑定watch返回的表单值,确保显示与RHF状态一致 - 去掉多余的
inputs1本地状态,统一用RHF管理表单数据 - 修复下拉框的错误提示文案,给Validate按钮添加手动校验逻辑
方案二:使用Controller组件(复杂受控场景推荐)
对于需要手动控制的组件,React-Hook-Form推荐使用Controller组件包裹,它能帮助RHF完全追踪组件的状态变化,避免自定义事件冲突。
核心代码片段:
// 从useForm解构control const { register, formState: { errors }, handleSubmit, control, setValue, trigger } = useForm({ defaultValues: { searchby: "searchby", customers: "", firstName: "" } }); // 下拉框变化时直接更新customers字段 const handleSelectChange = (event) => { const value = event.target.value; setInputs(value); setValue("customers", value); }; // 用Controller包裹customers输入框 <Controller name="customers" control={control} rules={{ required: true }} // 可添加校验规则 render={({ field }) => ( <input {...field} placeholder="Customer" /> )} />
优势:
Controller自动处理value和onChange,无需手动维护状态- 支持直接配置校验规则,与RHF的校验体系完全兼容
- 避免自定义事件与RHF内置事件的冲突
验证效果
提交表单时,customers字段会正确携带下拉框选中的值,表单数据能正常被RHF捕获。
内容的提问来源于stack exchange,提问作者kick
相关产品推荐
相关产品推荐

