使用react-hook-form校验表单时输入框无法输入、值丢失问题
问题根因
react-hook-form的register方法默认会向输入框注入onChange、onBlur、ref、name四个核心属性,用于在库内部托管表单状态、触发校验。
- 属性覆盖问题:JSX中写在后面的展开属性会覆盖前面的同名属性,你之前的写法把自定义
onChange放在{...register()}前面,register注入的内部onChange会直接覆盖你写的setName逻辑,外部state完全不会更新。 - 状态冲突问题:
react-hook-form默认采用非受控组件模式实现,内部单独维护一套表单值状态,你手动给输入框绑定外部state的value属性,会和库内部的值状态产生冲突,直接导致输入时视图不更新。
解决方法
优先选择第一种方案,符合库的设计初衷,维护成本最低。
- 方案1:使用react-hook-form全量托管表单状态,移除自定义state绑定
删掉输入框上自定义的value和onChange属性,所有表单值、校验逻辑都交给库处理,跨路由留存、提交取值都通过库提供的能力实现:- 初始化表单时通过
defaultValues配置初始值 - 用
watch方法监听全量表单值变化,同步更新到你之前用的useContext中,路由跳转后值不会丢失 - 提交时要么在当前页通过
handleSubmit拿到校验通过的表单值发请求,要么跳转后从useContext里取之前同步好的值提交
示例代码:
// 从context拿存值的方法 const { setRegisterFormData } = useContext(FormContext) const { register, handleSubmit, watch } = useForm({ defaultValues: { name: '', email: '', phone: '', birthday: '' } }) // 表单值变化时同步到全局context,跨路由不丢失 const allFormValues = watch() useEffect(() => { setRegisterFormData(allFormValues) }, [allFormValues, setRegisterFormData]) // 提交逻辑,data就是校验通过的全量表单值 const submitForm = (data) => { // 直接发起POST请求即可,比如 fetch('/api/register', {method:'POST', body: JSON.stringify(data)}) } // 输入框写法,不需要额外绑定value和onChange <input type="text" placeholder="Name *" {...register("name", {required: true, minLength: 2})} /> - 初始化表单时通过
- 方案2:保留自定义受控state,手动合并双端更新逻辑
如果你必须保留自己写的state管理逻辑,就把自定义属性放在{...register()}后面避免被覆盖,同时在onChange里同时触发库内部更新和你自己的state更新:
该方案需要给每个字段手动合并逻辑,冗余代码多,非必要不选。// 先拿到register绑定的属性 const nameField = register("name", {required: true, minLength: 2}) <input type="text" placeholder="Name *" {...nameField} // 自定义属性放在后面,覆盖register默认注入的同名属性 value={name} onChange={(e) => { nameField.onChange(e) // 先触发react-hook-form内部的值更新和校验 setName(e.target.value) // 再触发自己的state更新,同步到context }} />
内容的提问来源于stack exchange,提问作者ceaiius
相关产品推荐
相关产品推荐

