I'm trying to use React Select with useForm and got stuck when submitting form values.
My goal is a multi value react select with a default value and onChange function to do some validations when changing (like limit the number of itens to 3).
I already tried to search the solution in others posts and did make some changes in my code but unfortunately I did not succeed.
Everything seems to work perfectly but when I submit the form, my controller results in undefined value.
import React, {useEffect, useState, useContext} from 'react'
import {useForm, Controller} from 'react-hook-form'
import axios from 'axios';
import Select from 'react-select';
import BeeUtils from '../../../utils/BeeUtils'
export default function EditCategory2({place, goBack}){
var messageFieldRequired = 'Campo Obrigatório';
const audienceOptions = [
{ value: 'Lésbicas', label: 'Lésbicas' },
{ value: 'Gays', label: 'Gays' },
{ value: 'Bissexuais', label: 'Bissexuais' },
{ value: 'Transexuais', label: 'Transexuais' },
{ value: 'Queer', label: 'Queer' },
{ value: 'Intersexo', label: 'Intersexo' },
{ value: 'Assexual', label: 'Assexual' },
{ value: 'Héteros', label: 'Héteros' },
{ value: 'Todxs', label: 'Todxs' }
]
const handleAudienceSelector = (e) => {
console.log('OK');
console.log(e);
if(e.length > 3){
e.pop();
alert('max of 3 selected');
}
}
const {register , handleSubmit, errors, setValue, getValues, setError, control} = useForm();
const requestUpdate = async (data) => {
data.createdBy = place.createdBy;
data._id = place._id;
data.recordUpdatedType = 'audience';
console.log(data);
return;
}
const selectRequired = (e) => {
console.log(e);
console.log('OK-2');
//var error = e.length == 0? messageFieldRequired : '';
//return error;
}
const onSubmit = data => {
console.log(data)
requestUpdate(data);
}
return (
<div className='cad-form'>
<form onSubmit={handleSubmit(onSubmit)}>
<div className='cad-tit-container'>
<span className='cad-titulo'> Edit Here</span>
</div>
<div className='cad-container'>
<label htmlFor='test-audience'>Audience</label>
<Controller
name="test-audience"
control={control}
rules={{ validate: selectRequired }}
render={() => (
<Select
defaultValue={[audienceOptions[0], audienceOptions[1]]}
isMulti
onChange={handleAudienceSelector}
placeholder='Select Itens'
options={audienceOptions}
className="basic-multi-select selectCustom"
classNamePrefix="select"
/>
)}
/>
{errors?.targetAudience && <p>{errors.targetAudience.message}</p>}
</div>
<div className='btn-container'>
<div className='cad-btn'><button onClick={(e) => goBack('initial')} className="btn waves-effect yellow darken-2">Voltar</button></div>
<div className='cad-btn'><button type='submit' className="btn waves-effect yellow darken-2">Salvar Alterações</button></div>
</div>
</form>
</div>
)
}
After some changes (thanks to help of the answer) I tried this code
import React, {useEffect, useState, useContext} from 'react'
import {useForm, Controller} from 'react-hook-form'
import axios from 'axios';
import Select from 'react-select';
import BeeUtils from '../../../utils/BeeUtils'
export default function EditCategory2({place, goBack}){
var messageFieldRequired = 'Campo Obrigatório';
const audienceOptions = [
{ value: 'Lésbicas', label: 'Lésbicas' },
{ value: 'Gays', label: 'Gays' },
{ value: 'Bissexuais', label: 'Bissexuais' },
{ value: 'Transexuais', label: 'Transexuais' },
{ value: 'Queer', label: 'Queer' },
{ value: 'Intersexo', label: 'Intersexo' },
{ value: 'Assexual', label: 'Assexual' },
{ value: 'Héteros', label: 'Héteros' },
{ value: 'Todxs', label: 'Todxs' }
]
const handleAudienceSelector = (e) => {
console.log('OK');
console.log(e);
if(e.length > 3){
e.pop();
alert('max of 3 selected');
}
}
const {register , handleSubmit, errors, setValue, getValues, setError, control} = useForm();
const requestUpdate = async (data) => {
data.createdBy = place.createdBy;
data._id = place._id;
data.recordUpdatedType = 'audience';
console.log(data);
return;
}
const onSubmit = data => {
console.log(data)
requestUpdate(data);
}
return (
<div className='cad-form'>
<form onSubmit={handleSubmit(onSubmit)}>
<div className='cad-tit-container'>
<span className='cad-titulo'> Edit Here</span>
</div>
<div className='cad-container'>
<label htmlFor='test-audience'>Audience</label>
<Controller
name="targetAudience"
control={control}
defaultValue={[audienceOptions[0], audienceOptions[1]]}
rules={{ required: messageFieldRequired }}
render={({ field: { onChange, value } }) => (
<Select
value={value}
onChange={onChange}
isMulti
placeholder="Select Itens"
options={audienceOptions}
className="basic-multi-select selectCustom"
classNamePrefix="select"
/>
)}
/>
</div>
<div className='btn-container'>
<div className='cad-btn'><button onClick={(e) => goBack('initial')} className="btn waves-effect yellow darken-2">Voltar</button></div>
<div className='cad-btn'><button type='submit' className="btn waves-effect yellow darken-2">Salvar Alterações</button></div>
</div>
</form>
</div>
)
}
But now I got the error: TypeError: Cannot read property 'onChange' of undefined
The reason why it isn't working is because you forgot to to link the <Controller />
component with the <Select />
component via the value
and onChange
properties of the render
prop function from <Controller />
.
<Controller
name="targetAudience"
control={control}
defaultValue={[audienceOptions[0], audienceOptions[1]]}
rules={{ required: "Campo obrigatório", validate: isOnly3Values }}
render={({ field: { onChange, value } }) => (
<Select
value={value}
onChange={onChange}
isMulti
placeholder="Select Itens"
options={audienceOptions}
className="basic-multi-select selectCustom"
classNamePrefix="select"
/>
)}
/>
You also don't need to use useState
here for handling the error state as RHF already provides this functionality. For setting a field to be required, you can just set the required
property of the validation object which can be passed to <Controller />
via the rules
prop. Check here for more information about <Controller />
. I would suggest to also use the validate
function from RHF to check if the user added more than 3 items to your <Select />
and display an error message instead of using an alert.
I made some overall small changes and corrected some minor issues (e.g. the errors
object is located in the formState
property since v7). Feel free to write a comment if something isn't clear.