React-select defaultValue is undefined when submitted

Viewed 2734

I'm using react-select for a single select menu. The defaultValue appears to be displaying correctly when I edit the form, but when I submit the form the value returned is undefined if the selection is left unchanged. If I click the defaultValue then it works correctly when submitted. Is there an additional prop I'm missing to maintain the defaultValue through submission?

Code looks like this:

<Select
   closeMenuOnSelect={true}
   options={options()}
   defaultValue={defaultVal}
   className="basic-single"
   classNamePrefix="select"
/>

Where options() is a simple function to format the data like this:

  const options = [
    { value: "chocolate", label: "Chocolate" },
    { value: "strawberry", label: "Strawberry" },
    { value: "vanilla", label: "Vanilla" }
  ];

And defaultVal returns a single value by filtering options:

  let defaultVal = options().filter(
    (optionChoice) => optionChoice.label === props.choice
  );
1 Answers

Since you are using react-hook-form you need to utilize their Controller component to register your select with the form. Reference here: Integrating Controlled Inputs.

The following component declaration illustrates wrapping a react-select component in a react-hook-form Controller component. It is registered with react-hook-form using control={control} and then renders the select through the Controller component's render prop.

You also need to let react-hook-form know when the selection changes by calling handleChange from the select on change. handleChange() in turn calls react-hook-form's setValue() with the changed value and shouldDirty: true flagging the change.

const handleChange = (change) => {
  setValue("controlledSelect", change, {
    shouldDirty: true
  });
};

Working sandbox here: https://codesandbox.io/s/react-select-with-react-hook-form-im9hi

import React from "react";
import { useForm, Controller } from "react-hook-form";
import Select from "react-select";

function ControlledSelect() {
  const { handleSubmit, control, setValue } = useForm();
  const [options, setOptions] = React.useState(initialOptions);

  const onSubmitData = (data) => {
    console.log(data);
  };

  const handleChange = (change) => {
    setValue("controlledSelect", change, {
      shouldDirty: true
    });
  };

  return (
    <div className="container">
      <form onSubmit={handleSubmit(onSubmitData)}>
        <Controller
          name="controlledSelect"
          control={control}
          defaultValue={options[1]}
          render={() => (
            <Select
              options={options}
              defaultValue={options[1]}
              onChange={handleChange}
            />
          )}
        />
        <button type="submit">Submit Select</button>
      </form>
    </div>
  );
}

Here's a rough snippet showing a working select. It looks like you want to declare the defaultValue as an index in the array: defaultValue={defaultVal[0]}. This will necessitate generating your formatted options array before render rather than passing a function, which is probably better regardless.

<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>


<script src="https://cdn.jsdelivr.net/npm/emotion@9.2.12/dist/emotion.umd.min.js"></script>

<script src="https://unpkg.com/prop-types@15.5.10/prop-types.min.js"></script>
<script src="https://unpkg.com/react-input-autosize@2.2.1/dist/react-input-autosize.min.js"></script>
<script src="https://unpkg.com/react-select@2.1.2/dist/react-select.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<div id="App"></div>
<script type="text/babel">
  const {useRef} = React;
  
  const options = [
    { value: 'chocolate', label: 'Chocolate' },
    { value: 'strawberry', label: 'Strawberry' },
    { value: 'vanilla', label: 'Vanilla' }
  ]

function App() {
    const formRef = useRef();
    
    const submitForm = (e, ref) => {
      e.preventDefault();
      console.log(formRef.current.elements[1].value);

  }
  
    return (
      <form ref={formRef} onSubmit={submitForm}>
        <Select 
          name="select" 
          options={options} 
          defaultValue={options[1]} 
         />
        <input type="submit" value="Submit" />
      </form>
    )    
}

ReactDOM.render(<App />, document.getElementById('App'));
 </script>

Related