You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 15:10:33