admin管理员组

文章数量:1287866

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, { ponents } 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 (
      <ponents.Option {...props}>
        <div style={{ display: "inline-block", "min-width": "40px" }}>{props.data.suffix}</div>
        <div style={{ display: "inline-block" }}>{props.data.value}</div>
      </ponents.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" 
            ponents={{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, { ponents } from 'react-select';
import { useForm, Controller } from 'react-hook-form';
import { selectStyles } from "./styles"
import connectionTypes from "../../data/connection_types";
import { OptionWithSuffix } from "../form_ponents/ponents";

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}
              ponents={{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;  

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, { ponents } 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 (
      <ponents.Option {...props}>
        <div style={{ display: "inline-block", "min-width": "40px" }}>{props.data.suffix}</div>
        <div style={{ display: "inline-block" }}>{props.data.value}</div>
      </ponents.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" 
            ponents={{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, { ponents } from 'react-select';
import { useForm, Controller } from 'react-hook-form';
import { selectStyles } from "./styles"
import connectionTypes from "../../data/connection_types";
import { OptionWithSuffix } from "../form_ponents/ponents";

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}
              ponents={{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;  
Share Improve this question edited Jul 28, 2020 at 18:31 Eken asked Jul 25, 2020 at 11:55 EkenEken 1031 gold badge1 silver badge12 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 3

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>
   )}
/>

I cant quite test your code, since it has a few imports.

From what I can see, you will need to get the selected option value on the change event.

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

  const handleMultiChange = selectedOption => {
    setValue("platform", selectedOption.value);
  };

Also, I do not really know the react-select control, but it might be controlled internally, so you do not need the values state hook

See here

take a look at the documentation of react-hook-form, they show the proper way on how to register third party ui ponents, even has a example using react-select Integrating with UI libraries

本文标签: javascriptReact hook form setValue returns undefined with multi select (reactselect)Stack Overflow