I am trying to use a custom Material-UI Autocomplete
component and connect it to react-hook-form
.
TLDR: Need to use MUI Autocomplete with react-hook-form Controller without
defaultValue
My custom Autocomplete
component takes an object with the structure {_id:'', name: ''}
it displays the name and returns the _id
when an option is selected. The Autocomplete
works just fine.
<Autocomplete
options={options}
getOptionLabel={option => option.name}
getOptionSelected={(option, value) => option._id === value._id}
onChange={(event, newValue, reason) => {
handler(name, reason === 'clear' ? null : newValue._id);
}}
renderInput={params => <TextField {...params} {...inputProps} />}
/>
In order to make it work with react-hook-form
I've set the setValues
to be the handler for onChange
in the Autocomplete
and manually register the component in an useEffect
as follows
useEffect(() => {
register({ name: "country1" });
},[]);
This works fine but I would like to not have the useEffect
hook and just make use of the register somehow directly.
Next I tried to use the Controller
component from react-hook-form
to proper register the field in the form and not to use the useEffect
hook
<Controller
name="country2"
as={
<Autocomplete
options={options}
getOptionLabel={option => option.name}
getOptionSelected={(option, value) => option._id === value._id}
onChange={(event, newValue, reason) =>
reason === "clear" ? null : newValue._id
}
renderInput={params => (
<TextField {...params} label="Country" />
)}
/>
}
control={control}
/>
I've changed the onChange
in the Autocomplete
component to return the value directly but it doesn't seem to work.
Using inputRef={register}
on the <TextField/>
would not cut it for me because I want to save the _id
and not the name
HERE is a working sandbox with the two cases. The first with useEffect
and setValue
in the Autocomplete
that works. The second my attempt in using Controller
component
Any help is appreciated.
LE
After the comment from Bill with the working sandbox of MUI Autocomplete, I Managed to get a functional result
<Controller
name="country"
as={
<Autocomplete
options={options}
getOptionLabel={option => option.name}
getOptionSelected={(option, value) => option._id === value._id}
renderInput={params => <TextField {...params} label="Country" />}
/>
}
onChange={([, { _id }]) => _id}
control={control}
/>
The only problem is that I get an MUI Error
in the console
Material-UI: A component is changing the uncontrolled value state of Autocomplete to be controlled.
I've tried to set an defaultValue
for it but it still behaves like that. Also I would not want to set a default value from the options array due to the fact that these fields in the form are not required.
The updated sandbox HERE
Any help is still very much appreciated
The accepted answer (probably) works for the bugged version of Autocomplete. I think the bug was fixed some time after that, so that the solution can be slightly simplified.
This is very useful reference/codesandbox when working with react-hook-form and material-ui: https://codesandbox.io/s/react-hook-form-controller-601-j2df5?
From the above link, I modified the Autocomplete example:
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
const ControlledAutocomplete = ({ options = [], renderInput, getOptionLabel, onChange: ignored, control, defaultValue, name, renderOption }) => {
return (
<Controller
render={({ onChange, ...props }) => (
<Autocomplete
options={options}
getOptionLabel={getOptionLabel}
renderOption={renderOption}
renderInput={renderInput}
onChange={(e, data) => onChange(data)}
{...props}
/>
)}
onChange={([, data]) => data}
defaultValue={defaultValue}
name={name}
control={control}
/>
);
}
With the usage:
<ControlledAutocomplete
control={control}
name="inputName"
options={[{ name: 'test' }]}
getOptionLabel={(option) => `Option: ${option.name}`}
renderInput={(params) => <TextField {...params} label="My label" margin="normal" />}
defaultValue={null}
/>
control
is from the return value of useForm(}
Note that I'm passing null
as defaultValue
as in my case this input is not required. If you'll leave defaultValue
you might get some errors from material-ui library.
UPDATE:
Per Steve question in the comments, this is how I'm rendering the input, so that it checks for errors:
renderInput={(params) => (
<TextField
{...params}
label="Field Label"
margin="normal"
error={errors[fieldName]}
/>
)}
Where errors
is an object from react-hook-form
's formMethods
:
const { control, watch, errors, handleSubmit } = formMethods