Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React hook form setValue returns undefined with multi select (react-select)

I'm trying to build a form using React with select elements from react-select and the validation done using react-hook-form.

Upon change the value seems to successfully change, but when I submit the form the select value is undefined.

I have input fields and those work properly, but for my select and multi-select elements it fails.

New to React so very confused, tried to find an answer to this but not able to. Removed some of the code to keep the interesting parts, hopefully I got it right.

import React, { useState } from "react";
import Collapsible from "../collapsible/Collapsible"
import Select, { components } from 'react-select';
import axios from "axios";
import { useInput } from "../../hooks"
import { useForm } from 'react-hook-form';
import { selectStyles } from "./styles"
import allBots  from "../../data/bots";
import platforms from "../../data/platforms";
import connectionTypes from "../../data/connection_types";
import generatePayload from "./generate_payload";

const Connect = () => {
  const { setValue, register, handleSubmit, errors } = useForm();

  const [values, setValues] = useState({
    selectedOption: []
  });
  
  const CustomOption = props => {
    return (
      <components.Option {...props}>
        <div style={{ display: "inline-block", "min-width": "40px" }}>{props.data.suffix}</div>
        <div style={{ display: "inline-block" }}>{props.data.value}</div>
      </components.Option>
    );
  };

  const handleBotChange = selectedBot => {
    setValue("bots", selectedBot);
    setValues(selectedBot);
  };

  const handleMultiChange = selectedOption => {
    console.log(selectedOption);
    setValue("platform", selectedOption);
    setValues(selectedOption);
  };

  const onSubmit = (data) => {
    console.log(data);
    // outputs { "bots": undefined, "platform": undefined, username: "theinput" }
  }
  return (
    <Collapsible className="collapsible" header="Connect">
      <div className="collapsibleContent">
        <form onSubmit={handleSubmit(onSubmit)}>
          <Select
            className="reactSelect"
            name="platform"
            placeholder="Platform"
            value={values.platform}
            onChange={handleMultiChange}
            options={platforms}
            ref={e => register({ name: "platform", required: true })}
          />
          <Select
            isMulti
            name="bots"
            options={bots}
            onChange={handleBotChange}
            closeMenuOnSelect={false}
            blurInputOnSelect={false}
            className="basic-multi-select formItem"
            classNamePrefix="select" 
            components={{Option: CustomOption}}  
            styles={selectStyles}
            placeholder="Bots"
            ref={e => register({ name: "bots", required: true })}
          />
          <input
            name="username"
            ref={register({
              validate: value => value !== "illegal_value" || "Illegal value"
            })}
          />
          {errors.username && errors.username.message}

          <button type="submit">Submit</button>
        </form>
      </div>
    </Collapsible>
  );
}

export default Connect; 

Update: I decided to use the "Controller" functionality of react-hook-form to integrate it with react-select. This is how part my code ended up. I also use SocketIO events in order to dynamically populate the options in the dropdown list. This solution isn't perfect but it's something.

import React, { useState, useEffect } from "react";
import Collapsible from "../collapsible/Collapsible"
import Select, { components } from 'react-select';
import { useForm, Controller } from 'react-hook-form';
import { selectStyles } from "./styles"
import connectionTypes from "../../data/connection_types";
import { OptionWithSuffix } from "../form_components/components";

import socket from "../socket";

const Connect = () => {
  const { errors, control, watch, getValues, handleSubmit, register, setValue, reset } = useForm();
  const watchFields = watch(["connectionType"]);
  const [bots, setBots] = useState([]);
  const connectionType = getValues("connectionType") || {};
  
  useEffect(() => {
    socket.on("bots", data => {
      setBots(data.map(obj => ({...obj, suffix: obj.count, value: obj.name, label: obj.name})));
    });
    return () => {
      socket.off("bots");
    };
  }, []);

  const onSubmit = (data) => {
    console.log(data);
  }
  
  return(  
    <Collapsible className="collapsible" header="Connect">
      <div>
        <form onSubmit={handleSubmit(data => onSubmit(data))}>    
        <Controller
          control={control}
          as={<Select className="formItem" options={platforms} />}
          rules={{ required: true }}
          placeholder="Select platform"
          name="platform"
          styles={selectStyles}
          register={register}
          setValue={setValue}
        />
        <Controller
          control={control}
          as={<Select className="formItem" options={connectionTypes}/>}
          rules={{ required: true}}
          placeholder="Select connection type"
          name="connectionType"
          styles={selectStyles}
          register={register}
          setValue={setValue}
        />
        {errors.connectionType && "Connection type is required"}
        {connectionType.value === "specific" &&
          <Controller
          control={control}
          as={
            <Select 
              isMulti 
              className="formItem" 
              options={bots}
              closeMenuOnBlur={false}
              closeMenuOnSelect={false}
              components={{Option: OptionWithSuffix}}  
              styles={selectStyles}
            />
          }
          rules={{ required: true }}
          placeholder="Select bots"
          name="bots"
          rules={{ required: true }}
          register={register}
          setValue={setValue}
        />
        }
        {errors.platform && connectionType.value === "specific" && "Bots are required"}    
        <input className="formItem" type="submit" placeholder="Connect"/>
      </form>
    </div>
  </Collapsible>
  );
}
export default Connect;  
like image 397
Eken Avatar asked Jul 23 '26 13:07

Eken


1 Answers

Here's how I made my MUI-Select take multiple values using react-hook-forms in v6.14.1.

<Controller
    name="categories"
    type="select"
    control={control}
    defaultValue={categoryOptions[0]}
    render={({ onChange, onBlur, value }) => (
        <RHFSelect
             onChange={onChange}
             onBlur={onBlur}
             value={[...value]}  // this is what you need to do
             multiple={true}
             options={categoryOptions}
        />
    )}
/> 

RHFSelect is a reusable Select in my code. To select only a single value, you may do to like this -

value={value}

For v7.9.0, it can be done like this

<Controller
   name="colors"
   type="select"
   control={control}
   defaultValue={[]}
   render={({ field }) => (
      <Select
         {...field}
         multiple
      >
      /* Select ops */
      </Select>
   )}
/>
like image 200
Nishant Kohli Avatar answered Jul 25 '26 02:07

Nishant Kohli