Proper way to use react-hook-form Controller with Material-UI Autocomplete - reactjs

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

So, I fixed this. But it revealed what I believe to be an error in Autocomplete.
First... specifically to your issue, you can eliminate the MUI Error by adding a defaultValue to the <Controller>. But that was only the beginning of another round or problems.
The problem is that functions for getOptionLabel, getOptionSelected, and onChange are sometimes passed the value (i.e. the _id in this case) and sometimes passed the option structure - as you would expect.
Here's the code I finally came up with:
import React from "react";
import { useForm, Controller } from "react-hook-form";
import { TextField } from "#material-ui/core";
import { Autocomplete } from "#material-ui/lab";
import { Button } from "#material-ui/core";
export default function FormTwo({ options }) {
const { register, handleSubmit, control } = useForm();
const getOpObj = option => {
if (!option._id) option = options.find(op => op._id === option);
return option;
};
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
<Controller
name="country"
as={
<Autocomplete
options={options}
getOptionLabel={option => getOpObj(option).name}
getOptionSelected={(option, value) => {
return option._id === getOpObj(value)._id;
}}
renderInput={params => <TextField {...params} label="Country" />}
/>
}
onChange={([, obj]) => getOpObj(obj)._id}
control={control}
defaultValue={options[0]}
/>
<Button type="submit">Submit</Button>
</form>
);
}

import { Button } from "#material-ui/core";
import Autocomplete from "#material-ui/core/Autocomplete";
import { red } from "#material-ui/core/colors";
import Container from "#material-ui/core/Container";
import CssBaseline from "#material-ui/core/CssBaseline";
import { makeStyles } from "#material-ui/core/styles";
import TextField from "#material-ui/core/TextField";
import AdapterDateFns from "#material-ui/lab/AdapterDateFns";
import LocalizationProvider from "#material-ui/lab/LocalizationProvider";
import React, { useEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";
export default function App() {
const [itemList, setItemList] = useState([]);
// const classes = useStyles();
const {
control,
handleSubmit,
setValue,
formState: { errors }
} = useForm({
mode: "onChange",
defaultValues: { item: null }
});
const onSubmit = (formInputs) => {
console.log("formInputs", formInputs);
};
useEffect(() => {
setItemList([
{ id: 1, name: "item1" },
{ id: 2, name: "item2" }
]);
setValue("item", { id: 3, name: "item3" });
}, [setValue]);
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<Container component="main" maxWidth="xs">
<CssBaseline />
<form onSubmit={handleSubmit(onSubmit)} noValidate>
<Controller
control={control}
name="item"
rules={{ required: true }}
render={({ field: { onChange, value } }) => (
<Autocomplete
onChange={(event, item) => {
onChange(item);
}}
value={value}
options={itemList}
getOptionLabel={(item) => (item.name ? item.name : "")}
getOptionSelected={(option, value) =>
value === undefined || value === "" || option.id === value.id
}
renderInput={(params) => (
<TextField
{...params}
label="items"
margin="normal"
variant="outlined"
error={!!errors.item}
helperText={errors.item && "item required"}
required
/>
)}
/>
)}
/>
<button
onClick={() => {
setValue("item", { id: 1, name: "item1" });
}}
>
setValue
</button>
<Button
type="submit"
fullWidth
size="large"
variant="contained"
color="primary"
// className={classes.submit}
>
submit
</Button>
</form>
</Container>
</LocalizationProvider>
);
}

I do not know why the above answers did not work for me, here is the simplest code that worked for me, I used render function of Controller with onChange to change the value according to the selected one.
<Controller
control={control}
name="type"
rules={{
required: 'Veuillez choisir une réponse',
}}
render={({ field: { onChange, value } }) => (
<Autocomplete
freeSolo
options={['field', 'select', 'multiple', 'date']}
onChange={(event, values) => onChange(values)}
value={value}
renderInput={(params) => (
<TextField
{...params}
label="type"
variant="outlined"
onChange={onChange}
/>
)}
/>
)}

Thanks to all the other answers, as of April 15 2022, I was able to figure out how to get this working and render the label in the TextField component:
const ControlledAutocomplete = ({
options,
name,
control,
defaultValue,
error,
rules,
helperText,
}) => (
<Controller
name={name}
control={control}
defaultValue={defaultValue}
rules={rules}
render={({ field }) => (
<Autocomplete
disablePortal
options={options}
getOptionLabel={(option) =>
option?.label ??
options.find(({ code }) => code === option)?.label ??
''
}
{...field}
renderInput={(params) => (
<TextField
{...params}
error={Boolean(error)}
helperText={helperText}
/>
)}
onChange={(_event, data) => field.onChange(data?.code ?? '')}
/>
)}
/>
);
ControlledAutocomplete.propTypes = {
options: PropTypes.arrayOf({
label: PropTypes.string,
code: PropTypes.string,
}),
name: PropTypes.string,
control: PropTypes.func,
defaultValue: PropTypes.string,
error: PropTypes.object,
rules: PropTypes.object,
helperText: PropTypes.string,
};
In my case, options is an array of {code: 'US', label: 'United States'} objects. The biggest difference is the getOptionLabel, which I guess needs to account for if both when you have the list open (and option is an object) and when the option is rendered in the TextField (when option is a string) as well as when nothing is selected.

I have made it work pretty well including multiple tags selector as follow bellow. It will work fine with mui5 and react-hook-form 7
import { useForm, Controller } from 'react-hook-form';
import Autocomplete from '#mui/material/Autocomplete';
//setup your form and control
<Controller
control={control}
name="yourFiledSubmitName"
rules={{
required: 'required field',
}}
render={({ field: { onChange } }) => (
<Autocomplete
multiple
options={yourDataArray}
getOptionLabel={(option) => option.label}
onChange={(event, item) => {
onChange(item);
}}
renderInput={(params) => (
<TextField {...params} label="Your label" placeholder="Your placeholder"
/>
)}
)}
/>

Instead of using controller, with the help of register, setValue of useForm and value, onChange of Autocomplete we can achieve the same result.
const [selectedCaste, setSelectedCaste] = useState([]);
const {register, errors, setValue} = useForm();
useEffect(() => {
register("caste");
}, [register]);
return (
<Autocomplete
multiple
options={casteList}
disableCloseOnSelect
value={selectedCaste}
onChange={(_, values) => {
setSelectedCaste([...values]);
setValue("caste", [...values]);
}}
getOptionLabel={(option) => option}
renderOption={(option, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option}
</React.Fragment>
)}
style={{ width: "100%" }}
renderInput={(params) => (
<TextField
{...params}
id="caste"
error={!!errors.caste}
helperText={errors.caste?.message}
variant="outlined"
label="Select caste"
placeholder="Caste"
/>
)}
/>
);

Related

Material UI autocomplete with react-form-hook validation value is not changing properly

I am trying to implement multi-select Mui autocomplete.
Whenever user selects an option, I want Chip component to be displayed underneath.
I am using react-form-hook to check validation. categories field is an array, and I want it to have at least one item.
Problem is when I delete a chip, the value does not update properly. I am using react state to keep track of selectedItems so I can display Chip component.
When I delete the chip, it is deleted from selectedItem state, but actual value from Controller does not change.
Please review my code, and give me some feedbacks. Thank you all!!
mui_autocomplete
import React, { useEffect, useState } from 'react';
import { Autocomplete, Chip, FormControl, FormLabel, Stack, TextField } from '#mui/material';
import { Controller } from 'react-hook-form';
import CloseIcon from '#mui/icons-material/Close';
export default function FormAutoComplete({ name, control, label, error, ...props }) {
const [selectedItems, setSelectedItems] = useState([]);
const selectedItemChip = selectedItems.map((item) => {
return (
<Chip
key={item}
label={item}
deleteIcon={<CloseIcon />}
onDelete={() => {
setSelectedItems((prev) => prev.filter((entry) => entry !== item));
}}
/>
);
});
return (
<FormControl fullWidth>
<FormLabel>{label}</FormLabel>
<Controller
name={name}
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
multiple
filterSelectedOptions
options={options}
getOptionLabel={(option) => option}
renderTags={() => {}}
value={selectedItems}
onChange={(e, newValue) => {
const addedItem = newValue[newValue.length - 1];
setSelectedItems((prev) => [...prev, addedItem]);
onChange(selectedItems);
return selectedItems;
}}
renderInput={(params) => (
<TextField
{...params}
{...props}
error={!!error}
helperText={error && error.message}
/>
)}
/>
)}
/>
<Stack direction="row" marginTop={2} gap={1} flexWrap="wrap">
{selectedItemChip}
</Stack>
</FormControl>
);
}
export const options = [
'Building Materials',
'Tools',
'Decor & Furniture',
'Bath',
'Doors & Windows',
'Cleaning',
'Electrical',
'Heating & Cooling',
'Plumbing',
'Hardware',
'Kitchen',
'Lawn & Garden',
'Lighting & Fans',
];
Sandbox
Once chip is deleted, react-hook-form is not updated with latest value. So, when form is submitted, old data is logged.
No need to track selectedItems in a state variable. You can use useWatch hook to get latest value from react-hook-form
use setValue to update value in formState
With these changes your FormAutoComplete component should look like this
export default function FormAutoComplete({
name,
control,
label,
error,
setValue, // Pass this prop from parent component. Can be destructured from useForm hook
...props
}) {
// const [selectedItems, setSelectedItems] = useState([]);
const selectedItems = useWatch({control,name}) // import useWatch from react-hook-form
const selectedItemChip = selectedItems.map((item) => {
return (
<Chip
key={item}
label={item}
deleteIcon={<CloseIcon />}
onDelete={() => {
// setSelectedItems((prev) => [...prev.filter((entry) => entry !== item)]);
setValue(name,selectedItems.filter((entry) => entry !== item))
}}
/>
);
});
return (
<FormControl fullWidth>
<FormLabel>{label}</FormLabel>
<Controller
name={name}
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
multiple
filterSelectedOptions
options={options}
getOptionLabel={(option) => option}
renderTags={() => {}}
// value={selectedItems}
value={value}
onChange={(e, newValue) => {
// setSelectedItems(newValue);
onChange(newValue);
}}
renderInput={(params) => (
<TextField
{...params}
{...props}
error={!!error}
helperText={error && error.message}
/>
)}
/>
)}
/>
<Stack direction="row" marginTop={2} gap={1} flexWrap="wrap">
{selectedItemChip}
</Stack>
</FormControl>
);
}
Documentation :
https://react-hook-form.com/api/useform/setvalue
https://react-hook-form.com/api/usewatch

How to display initialValues for material-ui autocomplete field?

I use the autocomplete field of material-ui (v5) and formik to generate my forms.
On this form, I have some lists defined as constants.
I use an api to get the default value of this list.
This api returns only the "code" of the option but not its label.
<Formik
enableReinitialize
initialValues={initialFormValues}
validationSchema={Yup.object().shape({
[...]
<Autocomplete
error={Boolean(touched.civility && errors.civility)}
helperText={touched.civility && errors.civility}
label="Civility"
margin="normal"
name="civility"
onBlur={handleBlur}
onChange={(e, value) => setFieldValue('civility', value)}
options={civilities}
value={values.civility}
getOptionLabel={(option) =>
option.name ? option.name : ''
}
isOptionEqualToValue={(option, value) => option.code === value}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label={<Trans>Civility</Trans>}
/>
)}
/>
My parameter isOptionEqualToValue is good because even if the value is not displayed in the input, it is well selected in the list.
You can see that the input text field is empty:
But if I unroll the list, I can see that my "ms" value has been selected:
What should I do to make the input text contain the default value?
After cloned your snippet code above, the problem was in getOptionLabel, the option argument is a string value, so it hasn't a name property and appears empty string. Here is an online example codesandbox.
import { useState } from "react";
import { Formik, Form } from "formik";
import Autocomplete from "#material-ui/lab/Autocomplete";
import TextField from "#material-ui/core/TextField";
export default function App() {
const civilities = ["Mr", "Ms", "Other"];
const [values, setValues] = useState({
civility: "Ms"
});
const handleBlur = (e) => {
console.log("Blur:", e.target.value);
};
const setFieldValue = (type, value) => {
setValues((oldValues) => ({ ...oldValues, [type]: value }));
};
return (
<Formik err>
{({ errors, touched }) => (
<Form>
<Autocomplete
error={Boolean(touched.civility && errors.civility)}
helperText={touched.civility && errors.civility}
label="Civility"
margin="normal"
name="civility"
onBlur={handleBlur}
onChange={(e, value) => setFieldValue("civility", value)}
options={civilities}
value={values.civility}
isOptionEqualToValue={(option, value) => option.code === value}
renderInput={(params) => (
<TextField {...params} variant="outlined" label="Civility" />
)}
/>
</Form>
)}
</Formik>
);
}

Make options of Material-ui autocomplete component clickable links?

import React, { useEffect, useRef } from "react";
import TextField from "#material-ui/core/TextField";
import Autocomplete from "#material-ui/lab/Autocomplete";
export default function FreeSolo(props) {
const [vendors, setVendors] = React.useState([]);
const [value, setValue] = React.useState();
const nameRef = useRef();
useEffect(() => {
sendDataToParent();
}, [value]);
const sendDataToParent = async () => {
await props.parentFunction(value);
};
return (
<div style={{}}>
<Autocomplete
freeSolo
id="free-solo-2-demo"
options={props.vendorData.map((option) => option.name)}
renderInput={(params) => (
<TextField
{...params}
value={value}
required
inputRef={nameRef}
onChange={(e) => {
setValue(e.target.value);
sendDataToParent();
}}
label="Vendor Name"
margin="normal"
variant="standard"
InputProps={{ ...params.InputProps, type: "search" }}
/>
)}
/>
</div>
);
}
I tried to do it using renderOption but could not get it working. I need to have the options to be clickable links so that whenever user selects of the options, he is redirected to the link.
EDIT: Solved using renderOption
renderOption={(option) => (
<React.Fragment>
<span
style={{ cursor: "pointer" }}
onClick={() => {
window.location.href = `/allvendors/${option.id}`;
}}
>
{option.name} - Click to visit the Vendor
</span>
</React.Fragment>
)}
Instead of making the options clickable links, you can redirect to the link using the onChange prop of the Autocomplete component.
I'm assuming each option in your vendorData has a name and also a link e.g.
{
name: "Google",
link: "https://www.google.com"
}
To be able to access the link from this object in the Autocomplete component's onChange, you'll need to change the options map function to return the whole option. After this change, if you try to click to open the dropdown, it will throw an error because the option label needs to be a string (e.g. the option name) and not an object (e.g. option). So, we need to add the getOptionLabel prop and return the option.name.
Finally, in the onChange function, we set the window.location.href equal to the option.link, which changes the current page's URL to the link and directs the user to that link.
<div style={{}}>
<Autocomplete
freeSolo
id="free-solo-2-demo"
getOptionLabel={(option) => option.name}
options={props.vendorData.map((option) => option)}
onChange={(event: any, option: any) => {
window.location.href = option.link;
}}
renderInput={(params) => (
<TextField
{...params}
value={value}
required
inputRef={nameRef}
onChange={(e) => {
setValue(e.target.value);
sendDataToParent();
}}
label="Vendor Name"
margin="normal"
variant="standard"
InputProps={{ ...params.InputProps, type: "search" }}
/>
)}
/>
</div>

React Hook Form V7 - Material UI 5 Autocomplete: Lazy loaded values not validated

I use react-hook-form V7 with an Material UI 5 Autocomplete input. My lazy loaded values in the Autocomplete are not validated on first submit click (but on second). What's wrong?
useEffect(() => {
setItemList([
{ id: 1, name: "item1" },
{ id: 2, name: "item2" }
]);
setItemIdSelected(2);
}, []);
[...]
<Autocomplete
options={itemList}
getOptionLabel={(item) => (item.name ? item.name : "")}
getOptionSelected={(option, value) =>
value === undefined || value === "" || option.id === value.id
}
value={
itemIdSelected
? itemList.find((item) => item.id === itemIdSelected)
: ""
}
onChange={(event, items) => {
if (items !== null) {
setItemIdSelected(items.id);
}
}}
renderInput={(params) => (
<TextField
{...params}
{...itemsIdFormHookRest}
label="items"
margin="normal"
variant="outlined"
inputRef={itemsIdFormHookRef}
error={errors.itemsId ? true : false}
helperText={errors.itemsId && "item required"}
required
/>
)}
/>
[...]
Please have a look to the code on code sandbox:
You should wrap the Material UI Autocomplete with the Controller Component provided by React Hook Form. See this section in the documentation for further information.
I would also suggest to remove the useState saving the id, as you could also access the value via React Hook Form either through getValues or after the handleSubmit callback is called.
Here is the updated CodeSandbox.
How can i implement datepickers?
This works only with modal, but when typing yourself is not working.
<LocalizationProvider dateAdapter={AdapterDateFns} locale={ruLocale}>
<Controller
render={({ field }) => (
<DatePicker
value={field.value}
onChange={(value) => field.onChange(value)}
renderInput={(params) => (
<TextField {...params} />
)}
/>
)}
name={name}
control={control}
/>
</LocalizationProvider>
I handle react-hook-form with mui Autocomplete in the following way
"devDependencies": {
"#mui/material": "^5.8.3",
"react-hook-form": "^7.33.1",
}
const Country = [{ label: 'United Kingdem', value: 'uk' }];
<Controller
name={"country"}
control={control}
rules={{
required: true,
}}
render={({ field: { onChange, ..._field } }) => (
<Autocomplete
options={Country}
onChange={(_, data) => onChange(data.value)}
renderInput={(params) => {
return (
<TextField
{...params}
label="disableCloseOnSelect"
variant="standard"
/>
);
}}
{..._field}
/>
)}
/>

A component is changing an uncontrolled Autocomplete to be controlled

Can you tell me that why I'm getting error "A component is changing an uncontrolled Autocomplete to be controlled.
Elements should not switch from uncontrolled to controlled (or vice versa).
Decide between using a controlled or uncontrolled Autocomplete element for the lifetime of the component."
component :
function AutoComplete(props) {
const defaultProps = {
options: props.options,
getOptionLabel: option => option.name,
};
const handleChange = (e, value) => {
props.onChange(value);
};
return (
<Autocomplete
{...defaultProps}
renderInput={params => (
<TextField {...params} label={props.label} margin="normal" />
)}
onChange={handleChange}
value={props.value}
/>
);
}
calling autocomplte:
<Controller
control={control}
name = 'country'
as = {
<AutoComplete
options={countryOptions}
onChange={selectCountryHandler}
label="Country"
value={selectedCountry || ''}
/>
} />
how can I solve this error?
You ensured that the value property never had been undefined, but you had to do same for inputValue.
the "value" state with the value/onChange props combination. This state represents the value selected by the user, for instance when pressing Enter.
the "input value" state with the inputValue/onInputChange props combination. This state represents the value displayed in the textbox.
⚠️ These two state are isolated, they should be controlled independently.
Component becomes uncontrolled when inputValue property is undefined, and vice versa.
If in the following example you delete an empty string from
React.useState('') you'll get the same error message because inputValue during first render is undefined.
import React from 'react'
import TextField from '#material-ui/core/TextField'
import Autocomplete from '#material-ui/lab/Autocomplete'
const options = ['Option 1', 'Option 2']
export default function AutocompleteLab() {
const [value, setValue] = React.useState(options[0])
const [inputValue, setInputValue] = React.useState('')
return (
<div>
<div>{`value: ${value !== null ? `'${value}'` : 'null'}`}</div>
<div>{`inputValue: '${inputValue}'`}</div>
<br />
<Autocomplete
value={value}
onChange={(_, newValue) => {
setValue(newValue)
}}
inputValue={inputValue}
onInputChange={(_, newInputValue) => {
setInputValue(newInputValue)
}}
options={options}
style={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Name" variant="outlined" />}
/>
</div>
)
}
When no value is selected, you need to add || null to prevent the Autocomplete going into uncontrolled mode:
<Autocomplete {...props} value={props.value || null} />
If you pass value={undefined} to the Autocomplete component, it will start in "uncontrolled" mode, meaning it keeps its own internal state. Then if you later supply a value it raises the "A component is changing" error. But if you pass value={null}instead of value={undefined} that causes the Autocomplete to start in controlled mode. The Autocomplete will assume you will be providing the state, not keep its own, and the error goes away.
I solved this by removing the default value.
<Autocomplete
multiple
id="multiple-limit-tags"
options={(option) => option.label}
getOptionLabel={(option) => option}
// defaultValue={options || []}
renderInput={(params) => <TextField {...params} label="My Label" />}
/>
It wasn't obvious how to solve this, and the documentation doesn't help much either. I find it curious that a copy-pasted example from the documentation results in this error. I guess the example works because the choices are hard-coded.
Previous answer was absolutely correct, BUT I spend 20 minutes while I figure out that inputValue it should be value
So working example from me:
export default function AddModal(): ReactElement {
const [resource, setResource] = useState('one');
<Autocomplete
id="autocomplete"
options={['one', 'two']}
defaultValue={resource}
value={resource}
PopperComponent={StyledPopper}
onChange={(event, newInputValue) => setResource(newInputValue)}
renderInput={(params) => <TextField {...params} />}
/>
I had the same issue today, but I was able to solve it by providing a default value of null as well as providing a null value in case of it not existing. I'll leave the code that worked for me:
<Autocomplete
value={filters.tag || null}
defaultValue={null}
options={tags || []}
getOptionLabel={(option) => option}
renderInput={(params) => (
<TextField {...params} label="Search" variant='outlined' size='small' />
)}
fullWidth
onChange={(event, value) => {
if (value) {
setFilters({ ...filters, tag: value });
} else {
setFilters({ ...filters, tag: '' });
}
}}
/>
For me I fixed this issue by updated the onChange function and add || null rather then removing the default value since I still need it here is the code
<Box mt={2}>
<Controller
control={control}
name="thematic"
rules={{
required: 'Veuillez choisir une réponse',
}}
render={({ field: { onChange } }) => (
<Autocomplete
defaultValue={
questionData?.thematic ? questionData?.thematic : null
}
options={thematics}
getOptionLabel={(option) => option.name}
onChange={(event, values) => {
onChange(values || null)
}}
renderInput={(params) => (
<TextField
{...params}
label="Thématique"
placeholder="Thématique"
helperText={errors.thematic?.message}
error={!!errors.thematic}
/>
)}
/>
)}
/>
</Box>
For the autocomplete with options which is a simple array without object you can simply do it that way and u want get any issue
<Box mt={2}>
<Controller
control={control}
name="type"
rules={{
required: 'Veuillez choisir une réponse',
}}
render={({ field: { onChange, value } }) => (
<Autocomplete
freeSolo
options={['Champ', 'Sélection', 'Choix multiple']}
onChange={(event, values) => onChange(values)}
value={value}
renderInput={(params) => (
<TextField
{...params}
label="Type de la question"
variant="outlined"
onChange={onChange}
helperText={errors.type?.message}
error={!!errors.type}
/>
)}
/>
)}
/>
</Box>
the default value you can set it within useForm if you are using react-hook-form
const {
handleSubmit,
control,
watch,
register,
formState: { errors },
} = useForm({
defaultValues: {
...
type: questionData?.type ? mapperQuestionType[questionData?.type] : '',
},
})

Resources