I am using Material UI v4.
I have a component which takes all the users and show them in dropdown but I am not able to select default value. I tried following sandbox but it did not worked for me: [https://codesandbox.io/s/material-demo-4mhcj?file=/demo.js][CodeSandBox]
For ex- test#gmail.com
react-hook-form : ^7.20.5
I tried giving a default value to both Controller and Autocomplete but it does not work for the first time.
Any idea what I am doing wrong here?
// data is something like
// [{name: 'ABCDE', email: 'ABCDEEE#gmail.com'}, {name: 'eeeeee', email: 'eeeeee12#gmail.com'}]
const UserSelection = ({ data = [], onChangeHandler }) => {
const { control } = useForm({});
const selectedUserEmail = "ABCDEEE#gmail.com";
return (
<>
<Controller
name="combo-box-demo"
control={control}
defaultValue={data.find(item => item.email === selectedUserEmail)}
render={() =>
<Autocomplete
id="combo-box-demo"
size="small"
options={data}
defaultValue={data.find(item => item.email === selectedUserEmail)}
onChange={onChangeHandler}
getOptionLabel={option => option.name }
renderOption={option => option.name}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
placeholder="Select"
/>
)}
/>
}
/>
</>
);
};
export default UserSelection;
I found the answer, so I am posting here:
The problem was data which was coming is asynchronous and autocomplete was not able to render it properly.
For now I am using:
const UserSelection = ({ data = [], onChangeHandler }) => {
const selectedUser = {name: 'ABCDE', email: 'ABCDEEE#gmail.com'};
return (
<Autocomplete
id="combo-box-demo"
options={data}
defaultValue={selectedUser}
onInputChange={onChangeHandler}
getOptionLabel={option => option.name }
renderOption={option => option.name}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
placeholder="Select"
/>
)}
/>
);
};
export default UserSelection;
And where ever I am using above component, I use like
data ? <UserSelection/> : null
Also instead of using onChange I used onInputChange which helps in getting default value
then it works and loads default user, hope it helps to community
Related
So I have an async combo box that pulls options from an API. The options are just an Id and a description. This component is part of a form that I'm showing to add or edit data. What I'd like to see happen is have the option be empty when adding new data and to have the current value selected when editing. Instead, it simply shows the label.
This is my code that's almost a copypaste of the example from the docs.
export default function AsyncAutoComplete(props:AsyncAutoCompleteProps) {
const [open, setOpen] = React.useState(false);
const [options, setOptions] = React.useState<EntityWithIdAndDescription[]>([]);
const loading = open && options.length === 0;
React.useEffect(() => {
let active = true;
if (!loading) {
return undefined;
}
(async () => {
props.populateWith().then((options)=> {
if (active) {
setOptions(options);
}})
})();
return () => {
active = false;
};
}, [loading]);
React.useEffect(() => {
if (!open) {
setOptions([]);
}
}, [open]);
return (
<Autocomplete
id="async-autocomplete"
open={open}
onOpen={() => {
setOpen(true);
}}
onClose={() => {
setOpen(false);
}}
onChange={props.onChange}
getOptionSelected={(option, value) => option.id === value.id}
getOptionLabel={(option) => option.description}
options={options}
loading={loading}
renderInput={(params) => (
<TextField
{...params}
label={props.label}
variant="outlined"
margin="dense"
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{loading ? <CircularProgress color="inherit" size={20} /> : null}
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
/>
)}
/>
);
What I want is to pass an Id value to this component and have it show the description for that value as the selected option (after making the API call). Using defaultValue doesn't seem to work.
Any advice with either modifying this or taking a different approach would be helpful.
Looks like what you're after is a controlled component. There's an example of this in Material UI's Autocomplete demo under Playground called controlled.
I'm not sure how you're getting the id of the initial value you want to pass to the component to show that it's selected. It could be something like the following.
Create a separate state for the value you select from this Autocomplete in your parent component. In fact, I would not have a separate component called AsyncAutocomplete at all. This is so you control all your state in the parent component and the Autocomplete component becomes purely presentational.
After your API call is complete and the setOptions(options) is called, call setValue with the value that you would like to show selected. This must be of type EntityWithIdAndDescription.
Create an inline-function for the onChange prop of the Autocomplete component which takes a the second parameter as the EntityWithIdAndDescription | null type. This is what's required from Autocomplete's onChange. Call setValue with this parameter as the argument.
Pass options, value, onChange and loading as props into the Autocomplete component. The additional props I've passed over and above what you've done in your code are:
<Autocomplete
...
disabled={loading}
value={value}
...
/>
Let me know how you go
const [value, setValue] = useState<EntityWithIdAndDescription | null>(null); // (1)
const [options, setOptions] = useState<EntityWithIdAndDescription[]>([]);
const loading = options.length === 0;
useEffect(() => {
populateWith().then((options)=> {
setOptions(options);
})
// (2)
setValue({
id: "something",
description: "something",
})
return () => {};
}, []);
// (4)
<Autocomplete
id="async-autocomplete"
disabled={loading}
onChange={(event: any, newValue: EntityWithIdAndDescription | null) => {
setValue(newValue); // (3)
}}
getOptionSelected={(option, value) => option.id === value.id}
getOptionLabel={(option) => option.description}
options={options}
loading={loading}
value={value}
renderInput={(params) => (
<TextField
{...params}
label={"My Entities"}
variant="outlined"
margin="dense"
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{loading ? <CircularProgress color="inherit" size={20} /> : null}
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
/>
)}
/>
);
Code Sandbox
Here's an example with the Autocomplete in a separate component I called MyAutocomplete. It includes an API call and setting a value I want to be selected first.
https://codesandbox.io/s/autumn-silence-lkjrf
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>
I am working with material ui autocomplete.
<Autocomplete
multiple
id="tags-outlined"
options={options}
getOptionLabel={(option) => option && option.name}
value={catArray}
onChange={handleCategoryFilter}
filterSelectedOptions
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
placeholder="Categories"
/>
)}
/>
Firstly I want selected value not to show in options secondly I want to clear autocomplete. Can you please guide me how can I do these things.
I am clearing the values by setCatArray([]) but it is not workinh
You can use material ui autocomplete's default prop filterSelectedOptions. If true, it will hide the selected options from the list box.
For clearing the values of autocomplete, it gives default clear icon at the end of the select box. you can clear it from there.
Try my sandbox link. Also try some more material ui demos here.
UPDATE: If you want to manage clearing values you can use value prop of autocomplete and manage it in onchange by updating it's value.
<Autocomplete
multiple
id="tags-outlined"
options={top100Films}
value={values}
getOptionSelected={(option, value) => value.title === option.title}
getOptionLabel={(option) => option.title}
filterSelectedOptions
onChange={(e, valueTags) => {
e.preventDefault();
const valuesArray = [];
valueTags.forEach((valueTag) => {
valuesArray.push({
title: top100Films
.filter((tag) => valueTag.title === tag.title)
.shift().title
});
});
setValues(valuesArray);
}}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="filterSelectedOptions"
placeholder="Favorites"
/>
)}
/>
Have you tried adding autocomplete=false to the list of properties in the component?
If you want to filter out selected options from autocomplete. Pass filterSelectedOptions={true} to your component
Secondly, you can clear the value by taking control of the selected values and setting it to an empty array. Here is my complete example
const options = ["one", "two", "three", "four"];
export default function MyAutocomplete() {
const [values, setValues] = React.useState<string[]>([]);
const onChange = (_, value) => {
setValues(value);
};
const clearSelected = () => {
setValues([]);
};
return (
<>
<button onClick={clearSelected}>Clear selected</button>
<Autocomplete
multiple
id="tags-outlined"
options={options}
getOptionLabel={(option) => option}
value={values}
onChange={onChange}
filterSelectedOptions
renderInput={(params) => (
<TextField {...params} variant="outlined" placeholder="Categories" />
)}
/>
</>
);
}
Live Example
const options = ["one", "two", "three", "four"];
export default function MyAutocomplete() {
const [values, setValues] = React.useState([]);
const onChange = (_, value) => {
setValues(value);
};
const clearSelected = () => {
setValues([]);
};
return (
<>
<button onClick={clearSelected}>Clear selected</button>
<Autocomplete
multiple
id="tags-outlined"
options={options}
getOptionLabel={(option) => option}
value={values}
onChange={onChange}
filterSelectedOptions
renderInput={(params) => (
<TextField {...params} variant="outlined" placeholder="Categories" />
)}
/>
</>
);
}
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] : '',
},
})
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"
/>
)}
/>
);