32

I'm using react-hook-forms Controller api around AsyncSelect from react-select to load options as the user types from an external API. Everything works fine except the returned value is coming back as the string "[object Object]" instead of the fullName property from the object.

My component:

           <Controller
            control={control}
            name="businessCategory"
            as={
              <AsyncSelect
                className="react-select-container"
                loadOptions={v => handleAutocompleteLookup(v)}
                onChange={handleCategoryInputChange}
                getOptionLabel={option => option.name}
                getOptionValue={option => option.fullName}
              />
            }
          />

My handleChange function. SetValue is from react-hook-form:

  const handleCategoryInputChange = newValue => {
    return setValue('businessCategory', newValue, true);
  };

Any my data is an array of objects with the following shape:

{
  fullName: "DJ service"
  id: "gcid:dj"
  name: "DJ service"
  publisher: "GMB"
}

Any clues on this would be appreciated, thank you!

9 Answers 9

58

Update your code in following way

In your import

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

In your hook component

function Yourcomponent(props){

    const methods = useForm();
    const { handleSubmit } = methods;
    

    const options = [
     { value: '1', label: 'Apple'},
     { value: '2', label: 'Ball'},
     { value: '3', label: 'Cat'},
    ];
    
    const default_value = 1; // you can replace with your default value
    

    // other codes of the component 
    

    function submitHandler(formData){
    
    // values are available in formData

    }


    return(
      <div>
        
        {* other part of your component *}
        <form onSubmit={handleSubmit(submitHandler)} >

           {* other part of your form *}
            <Controller
                control={methods.control}
                defaultValue={default_value}
                name="field_name_product"
                render={({ onChange, value, name, ref }) => (
                    <Select
                        inputRef={ref}
                        classNamePrefix="addl-class"
                        options={options}
                        value={options.find(c => c.value === value)}
                        onChange={val => onChange(val.value)}
                    />
                )}
            />

           {* other part of your form *}
        </form>

        {* other part of your component *}
      </div>
    )
}
Sign up to request clarification or add additional context in comments.

3 Comments

This solution had a problem with the onChange function for me - what fixed it was using render={({ field }) and then on the onChange property using onChange={val => field.onChange(val.value)}. Maybe im using a newer version of react-hook-form, but the onChange is now a property of the field object.
but "inputRef" prop is not available for react-select input react-select.com/props
you can just go for ref={ref}
20

For multi-select (works with react-hook-form v7):

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

const {
  control
} = useForm();

<Controller
  control={control}
  defaultValue={options.map(c => c.value)}
  name="options"
  render={({ field: { onChange, value, ref }}) => (
    <Select
      inputRef={ref}
      value={options.filter(c => value.includes(c.value))}
      onChange={val => onChange(val.map(c => c.value))}
      options={options}
      isMulti
    />
  )}
/>

2 Comments

onChange={(val) => onChange(val.value)} worked for me.
This is the closest I got myself (with Ali Aref's modification). The only issue is that the Controller's onChange function is never triggered :(
7
import Select from "react-select";
import { useForm, Controller } from "react-hook-form";
const options = [
     { value: '1', label: 'Apple'},
     { value: '2', label: 'Ball'},
     { value: '3', label: 'Cat'},
    ];
const {control} = useForm();
<Controller
 control={control}
 name="AnyName"
 render={({ field: { onChange, value, name, ref } }) => (
        <Select
        inputRef={ref}
        classNamePrefix="addl-class"
        options={options}
        value={options.find((c) => c.value === value)}
        onChange={(val) => onChange(val.map((c) =>c.value))}
        isMulti
        />
        )}
/>

Comments

6

I made it work like this:

const options = [
    {value: '', label: ''}
]
import { useForm, Controller } from "react-hook-form";

import { ErrorMessage } from "@hookform/error-message";

import Select from "react-select";


const methods = useForm();


<section>
    <label>Label</label>

    <Controller
      as={Select}
      name="Name"
      options={options}
      isMulti
      control={methods.control}
    />

    <ErrorMessage errors={methods.errors} name="Name" />
</section>

2 Comments

I can confirm this works a charm. Just to add for others, pass in all your Select props into the Controller instead.
It's throwing TypeError: props.render is not a function on Controller
6

well, actually most of these answers don't work for situations where you have a custom onChange function. The trick (seems like a bug) is to set the "onChange(val.value)" inline first and then set your state variable. Here is the full code I have that works;

 import { useForm, Controller } from "react-hook-form";
   const {
       register,
       control,
       formState: { errors },
       handleSubmit,
    } = useForm();

   const handleChangeType = (option) => {
      setItemType(option);
      var options = getOptions(option.value); 
      setList(options);
      setGender(null);
   }; 

    <Controller
      control={control}
      name="itemType"
      rules={{
         required: {
            value: assetType.value == "item",
            message: "Item type is required.",
         },
      }}
      render={({ field: { onChange, value, ref, name } }) => (
         <Select 
            className={"react-select"}
            classNamePrefix={"react-select"}
            placeholder={"Item type"} 
            options={itemTypeList}  
            onChange={val => {
                onChange(val.value);
                handleChangeType(val); 
            }} 
         />
      )}
   /> 
   {errors.item?.message && <div class="validationText">{errors.item?.message}</div>}

1 Comment

I was looking for a onChange solution for update a state(context), thanks! In this example is possible use the 'name' atribute on handleChangeType too.
2

I wanted to post what worked for me. I used react-select, react-hook-form, and @hookform/error-message:

I created a function as follows

  const handleCategoryInputChange = (newValue: any) => {
    console.log(newValue.value);
    return setValue('contract', newValue.value);
  };

My react select looks like this:

<Controller
  name="contract"
  control={control}
  rules={{ required: true }}
  render={({ field: { onChange, value, name, ref } }) => (
    <ReactSelect
     // value={contractData.find((c) => c.value === value)}
     // onChange={(val) => onChange(val)}
     onChange={handleCategoryInputChange}
     options={contractData}
     ref={ref}
     name={name}
     defaultValue={contractData[0]}
     theme={customTheme}
     isSearchable={false}
     className="block w-full min-w-0 flex-1 sm:text-sm"
/>
)}
/>
<ErrorMessage errors={errors} name="contract" message="Select a Contract" />

I don't think my className is working properly you can ignore that part. It is working for my specific set up though lol. The commented out code did not work the way I wanted because when i console logged the data I got it as follow:

contract: {label: 'contract name', value: 'contract name'}
optionPeriods: "dfd"
performanceEndDate: "2022-12-08"
performanceStartDate: "2022-12-08"
proposalDueDate: "2022-12-08T12:54"
requirements: "dfd"
trackingNumber: "dfd"

so I commented out that specific solution and did it the way you see now and i get the exact data i want for my contract as you can see below:

contract: "contract name"
optionPeriods: "dfd"
performanceEndDate: "2022-12-08"
performanceStartDate: "2022-12-08"
proposalDueDate: "2022-12-08T12:54"
requirements: "dfd"
trackingNumber: "dfd"

I hope this helps

Comments

2

What worked for me is:

  • react-hook-form v7.45.2
  • react-select 5.7.4
import { useForm, SubmitHandler, Controller } from "react-hook-form"

const { register } = useForm();

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

return (
    <Controller
        control={control}
        defaultValue='give_it_a_default'
        name="give_it_a_name"
        rules={{ required: true }}
        render={({ field, value, name, ref }) => (
            <Select
                name={field.name}
                inputRef={field.ref}
                options={options}
                value={options.find((c) => c.value === field.value)}
                onChange={val => field.onChange(val.value)}
            />)
      )}
    />
)

Comments

0

Fixed this by approaching it with a custom register via react-hook-form as seen here:

https://react-hook-form.com/get-started#Registerfields

1 Comment

Can you please share more details?
0

Use register instead:

const { control, handleSubmit, setValue, register } = useForm();
register('Control_name');
<Select
  options={options}
  onChange={(selected: any) => {
    if (selected) {
      setValue('Control_name', selected.value);
    }
  }}
/>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.