react-hook-form与react-select集成报错:无法读取undefined的'name'属性
React-Hook-Form 与 React-Select 集成问题解决
问题原因分析
1. "Cannot read properties of undefined (reading 'name')" 错误
react-hook-form 的 register 方法返回的 onChange 函数,期望接收标准 DOM 事件对象(包含 target.name 和 target.value),但 react-select 的 onChange 回调返回的是选中的选项对象({value: string, label: string}),直接传递该对象会导致 onChange 函数找不到 target 属性,进而触发报错。
2. 必填提示无法正常消失
InputSelect 组件内部维护了独立的 selectedOption 状态,与 react-hook-form 的表单状态脱节,导致表单无法正确感知选项是否被选中,因此必填校验始终不通过。
解决方案
核心是使用 react-hook-form 提供的 Controller 组件集成第三方表单控件,它能在第三方组件和表单状态之间建立同步桥梁,替代直接使用 register。
步骤1:修改 InputSelect 组件
移除组件内部状态,改为接收外部传入的 value 和 onChange,并做好值格式的双向转换:
// InputSelect.tsx import React from "react"; import clsx from "clsx"; import { default as ReactSelect } from "react-select"; export type SelectOption = { value: string; label: string; key?: string; }; export type InputSelectProps = { name: string; onChange: (value: string) => void; value: string | null; options: SelectOption[]; error?: string; label?: string; id?: string; }; const InputSelect: React.FC<InputSelectProps> = React.forwardRef( ({ id, options, name, onChange, label = "", error, value }, ref: React.Ref<any>) => { const prefix = React.useId(); const inputId = id ?? `${prefix}-${name}`; const isError = Boolean(error); // 将react-select的选项对象转换为表单需要的字符串值 const handleChange = (selectedOption: SelectOption | null) => { onChange(selectedOption ? selectedOption.value : null); }; // 将表单的字符串值转换为react-select需要的选项对象 const selectedOption = options.find(opt => opt.value === value) || null; return ( <div className={clsx("c-form-field")}> <label className={clsx("c-form-field__label c-label pb-2.5")} htmlFor={inputId} > {label} </label> <ReactSelect name={name} options={options} onChange={handleChange} value={selectedOption} /> {/* Error messages */} {isError && <p className="text-danger">{error}</p>} </div> ); } ); export default InputSelect;
步骤2:在 App 中用 Controller 替代 register
用 Controller 包裹 InputSelect,将表单状态的控制权完全交给 react-hook-form:
// App.tsx import React from "react"; import { useForm, Controller } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import * as Yup from "yup"; import InputSelect from "./InputSelect"; enum OrganizationRole { ADMIN = "ADMIN", MANAGER = "MANAGER", USER = "USER" } interface FormData { email: string; role: string; } const options = Object.values(OrganizationRole).map((role) => ({ value: role, label: role })); const App: React.FC = () => { const { handleSubmit, control, register, formState: { errors } } = useForm<FormData>({ resolver: yupResolver( Yup.object({ email: Yup.string().email("Invalid email address").required("Required"), role: Yup.string().required("Required") }) ), defaultValues: { email: "", role: "" } }); const onSubmit = (data: FormData) => { console.log(data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <div> <input type="email" {...register("email")} /> {errors.email && <p>Email est requis</p>} </div> <div> <Controller name="role" control={control} render={({ field: { value, onChange } }) => ( <InputSelect label={"Role"} error={errors.role?.message} options={options} value={value || null} onChange={onChange} /> )} /> {errors.role && <p>Rôle est requis</p>} </div> <button type="submit">Envoyer</button> </form> ); }; export default App;
关键改动说明
- 用
Controller替代register:专门适配第三方非标准表单组件,自动处理值同步和事件转换。 - 统一值格式:在组件层完成 react-select 选项对象与表单字符串值的双向转换,确保两边状态一致。
- 移除内部状态:让 react-hook-form 统一管理表单状态,彻底解决状态脱节导致的校验问题。
内容的提问来源于stack exchange,提问作者tomakazub
相关产品推荐
相关产品推荐

