skip to Main Content

I cannot find a solution to make react-select and react-hook-form work.

I constantly get an error: Cannot read properties of undefined (reading ‘name’) and an error telling me that my field is required.

Here is my code in codesandbox: https://codesandbox.io/s/funny-elbakyan-h5xz8w?file=/src/App.tsx:0-1347

Below is my code:

// InputSelect.tsx
import React from "react";
import clsx from "clsx";
import { default as ReactSelect, MenuPlacement } from "react-select";

export type SelectOption = {
  value: string;
  label: string;
  key?: string;
};

export type InputSelectProps = {
  name: string;
  onChange: (value: any) => void;
  options: SelectOption[];
  error?: string;
};

const InputSelect: React.FC<InputSelectProps> = React.forwardRef(
  ({ id, options, name, onChange, label = "", error }, ref: React.Ref<any>) => {
    const prefix = React.useId();
    const inputId = id ?? `${prefix}-${name}`;
    const isError = Boolean(error);
    const [
      selectedOption,
      setSelectedOption
    ] = React.useState<SelectOption | null>(null);

    const handleChange = (event: any) => {
      console.log(event);
      setSelectedOption(event);

      // BUG is here - Cannot read properties of undefined (reading 'name')
      onChange(event);
    };

    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={(selectedOption) => {
            handleChange(selectedOption);
          }}
          value={selectedOption}
        />
        {/* Error messages */}
        {isError && <p className="text-danger">{error}</p>}
      </div>
    );
  }
);

export default InputSelect;


// App.tsx
import React from "react";
import { useForm } 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,
    register,
    formState: { errors }
  } = useForm<FormData>({
    resolver: yupResolver(
      Yup.object({
        email: Yup.string().email("Invalid email address").required("Required"),
        role: Yup.string().required("Required")
      })
    )
  });

  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>
        <InputSelect
          label={"Role"}
          error={errors.role?.message}
          options={options}
          required
          {...register("role")}
        />
        {errors.role && <p>Rôle est requis</p>}
      </div>
      <button type="submit">Envoyer</button>
    </form>
  );
};

export default App;

2

Answers


  1. The react-select library is not returning the actual input element, which is what react-hook-form relies on. You can resolve the problem using a pseudo event like this:

    let pseudoEvent = { target: { name: "role", value: event.value } };
    
    onChange(pseudoEvent);
    

    To better understand what react-select does with a normal <input />, intercept your email input onChange and analyze the event.

    const emailHandler = register('email');
    
    const onEmailChange = (event: any) => {
      console.log(event);
    }
    
    <input type="email" {...emailHandler} onChange={onEmailChange} />
    
    Login or Signup to reply.
  2. react-select onChange return selected object and you react hook form can get that if you use prop like {…other} for your input.
    if you want control that manually and call rhf onChange get react hook form all props as name like field and call field.onChange in this format your input didn’t get any correct props

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search