Автодополнение — это обычный ввод текста, дополненный панелью предлагаемых опций.
Этот виджет используется для установки значения однострочного текстового поля. Он полезен в одном из двух случев:
- Значение для текстового поля должно быть выбрано из предопределенного набора допустимых значений, например, поле местоположения должно содержать действительное имя местоположения: поле со списком.
- Текстовое поле может содержать любое произвольное значение, но целесообразно предлагать пользователю возможные значения. Например, поле поиска может предлагать аналогичные или предыдущие поиски, чтобы сэкономить время пользователя: free solo.
It’s meant to be an improved version of the «react-select» and «downshift» packages.
- Обратная связь
- WAI-ARIA
- Размер пакета
- Figma
- Adobe
- Sketch
Комбо-Бокс
Значение должно быть выбрано из предопределенного набора допустимых значений.
<Autocomplete
disablePortal
id="combo-box-demo"
options={top100Films}
sx={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Movie" />}
/>
Песочница
By default, the component accepts the following options structures:
const filterOptions = createFilterOptions({
matchFrom: 'start',
stringify: option => option.title,
});
<Autocomplete filterOptions={filterOptions} />
например:
const options = [
{ label: 'The Godfather', id: 1 },
{ label: 'Pulp Fiction', id: 2 },
];
// or
const options = ['The Godfather', 'Pulp Fiction'];
However, you can use different structures by providing a getOptionLabel
prop.
Песочница
Каждый из следующих примеров демонстрирует одну функцию компонента автозаполнения.
Выбор страны
Choose one of the 248 countries.
Controllable states
The component has two states that can be controlled:
- 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.
value: ‘Option 1’
inputValue: »
Произвольное значение
Установите для freeSolo
значение true, чтобы текстовое поле могло содержать любое произвольное значение.
Ввод для поиска
Вы также можете показать диалоговое окно, если пользователь хочет добавить новое значение.
Создаваемый
If you intend to use this mode for a combo box like experience (an enhanced version of a select element) we recommend setting:
selectOnFocus
to helps the user clear the selected value.clearOnBlur
to helps the user to enter a new value.handleHomeEndKeys
to move focus inside the popup with the Home and End keys.- A last option, for instance
Add "YOUR SEARCH"
.
Вы также можете показать диалоговое окно, если пользователь хочет добавить новое значение.
Сгруппированные
You can group the options with the groupBy
prop. If you do so, make sure that the options are also sorted with the same dimension that they are grouped by, otherwise you will notice duplicate headers.
<Autocomplete
id="grouped-demo"
options={options.sort((a, b) => -b.firstLetter.localeCompare(a.firstLetter))}
groupBy={(option) => option.firstLetter}
getOptionLabel={(option) => option.title}
sx={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="With categories" />}
/>
Отключенные опции
<Autocomplete
id="disabled-options-demo"
options={timeSlots}
getOptionDisabled={(option) =>
option === timeSlots[0] || option === timeSlots[2]
}
sx={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Disabled options" />}
/>
useAutocomplete
For advanced customization use cases, we expose a headless useAutocomplete()
hook. It accepts almost the same options as the Autocomplete component minus all the props related to the rendering of JSX. The Autocomplete component uses this hook internally.
import useAutocomplete from '@material-ui/core/useAutocomplete';
- 4.5 4,5 кБ в сжатом виде.
Customized hook
Head to the Customized Autocomplete section for a customization example with the Autocomplete
component instead of the hook.
Асинхронные запросы
The component supports two different asynchronous use-cases:
- Load on open: it waits for the component to be interacted with to load the options.
- Search as you type: a new request is made for each keystroke.
Load on open
It displays a progress state as long as the network request is pending.
Search as you type
If your logic is fetching new options on each keystroke and using the current value of the textbox to filter on the server, you may want to consider throttling requests.
Additionally, you will need to disable the built-in filtering of the Autocomplete
component by overriding the filterOptions
prop:
import matchSorter from 'match-sorter';
const filterOptions = (options, { inputValue }) =>
matchSorter(options, inputValue);
<Autocomplete filterOptions={filterOptions} />
Места Google Maps
A customized UI for Google Maps Places Autocomplete.
For this demo, we need to load the Google Maps JavaScript API.
⚠️ Перед началом использования API карт Google, JavaScript необходимо зарегистрировать и создать учетную запись для выставления счетов.
Множественные значения
Also known as tags, the user is allowed to enter more than one value.
Фиксированные опции
В случае, если вам нужно зафиксировать определенный тег (так что он не мог быть удалён через интерфейс), вы можете установить chips в состояние disabled.
Чекбоксы
Limit tags
You can use the limitTags
prop to limit the number of displayed options when not focused.
<Autocomplete
multiple
limitTags={2}
id="multiple-limit-tags"
options={top100Films}
getOptionLabel={(option) => option.title}
defaultValue={[top100Films[13], top100Films[12], top100Films[11]]}
renderInput={(params) => (
<TextField {...params} label="limitTags" placeholder="Favorites" />
)}
/>
Размеры
Fancy smaller inputs? Use the size
prop.
Кастомизация
Custom input
The renderInput
prop allows you to customize the rendered input. The first argument of this render prop contains props that you need to forward. Pay specific attention to the ref
and inputProps
keys.
GitHub’s picker
This demo reproduces the GitHub’s label picker:
Head to the Customized hook section for a customization example with the useAutocomplete
hook instead of the component.
Основные моменты
The following demo relies on autosuggest-highlight, a small (1 kB) utility for highlighting text in autosuggest and autocomplete components.
Пользовательский фильтр
The component exposes a factory to create a filter method that can provided to the filterOptions
prop. You can use it to change the default option filter behavior.
import { createFilterOptions } from '@material-ui/core/Autocomplete';
createFilterOptions(config) => filterOptions
Аргументы
config
(Object [optional]):
config.ignoreAccents
(Boolean [optional]): Defaults totrue
. Remove diacritics.config.ignoreCase
(Boolean [optional]): Defaults totrue
. Lowercase everything.config.limit
(Number [optional]): Default to null. Limit the number of suggested options to be shown. For example, ifconfig.limit
is100
, only the first100
matching options are shown. It can be useful if a lot of options match and virtualization wasn’t set up.config.matchFrom
(‘any’ | ‘start’ [optional]): Defaults to'any'
.config.stringify
(Func [optional]): Controls how an option is converted into a string so that it can be matched against the input text fragment.config.trim
(Boolean [optional]): По умолчанию —false
. Remove trailing spaces.
Возвращает
filterOptions
: the returned filter method can be provided directly to the filterOptions
prop of the Autocomplete
component, or the parameter of the same name for the hook.
In the following demo, the options need to start with the query prefix:
const filterOptions = createFilterOptions({
matchFrom: 'start',
stringify: (option) => option.title,
});
<Autocomplete filterOptions={filterOptions} />;
Дополнительные параметры
For richer filtering mechanisms, like fuzzy matching, it’s recommended to look at match-sorter. Например:
import matchSorter from 'match-sorter';
const filterOptions = (options, { inputValue }) => matchSorter(options, inputValue);
<Autocomplete filterOptions={filterOptions} />;
Виртуализация
Поиск в 10000 случайно сгенерированных опций. Список виртуализирован благодаря реагирующему окну.
<Autocomplete
id="virtualize-demo"
sx={{ width: 300 }}
disableListWrap
PopperComponent={StyledPopper}
ListboxComponent={ListboxComponent}
renderGroup={renderGroup}
options={OPTIONS}
groupBy={(option) => option[0].toUpperCase()}
renderInput={(params) => <TextField {...params} label="10,000 options" />}
renderOption={(props, option) => (
<li {...props}>
<Typography noWrap>{option}</Typography>
</li>
)}
/>
Events
If you would like to prevent the default key handler behavior, you can set the event’s defaultMuiPrevented
property to true
:
<Autocomplete
onKeyDown={(event) => {
if (event.key === 'Enter') {
// Prevent's default 'Enter' behavior.
event.defaultMuiPrevented = false;
// your handler code
}
}}
/>
Ограничения
Автозаполнение
The browsers have heuristics to help the users fill the form inputs. However, it can harm the UX of the component.
By default, the component disables the autocomplete feature (remembering what the user has typed for a given field in a previous session) with the autoComplete="off"
attribute. Google Chrome does not currently support this attribute setting (Issue 587466). A possible workaround is to remove the id
to have the component generate a random one.
In addition to remembering past entered values, the browser might also propose autofill suggestions (saved login, address, or payment details). In the event you want the avoid autofill, you can try the following:
-
Name the input without leaking any information the browser can use. e.g.
id="field1"
instead ofid="country"
. If you leave the id empty, the component uses a random id. -
Set
autoComplete="new-password"
(some browsers will suggest a strong password for inputs with this attribute setting):<TextField {...params} inputProps={{ ...params.inputProps, autoComplete: 'new-password', }} />
Read the guide on MDN for more details.
iOS VoiceOver
VoiceOver on iOS Safari doesn’t support the aria-owns
attribute very well. You can work around the issue with the disablePortal
prop.
ListboxComponent
If you provide a custom ListboxComponent
prop, you need to make sure that the intended scroll container has the role
attribute set to listbox
. This ensures the correct behavior of the scroll, for example when using the keyboard to navigate.
Доступность
(WAI-ARIA: https://www.w3.org/TR/wai-aria-practices/#combobox)
We encourage the usage of a label for the textbox. The component implements the WAI-ARIA authoring practices.
The Autocomplete component is useful for giving the user limited options to select from. When a choice is selected it populates an Input subcomponent. Autocomplete has a ‘clear’ button for removing the selected choice.
In this post we will:
- Add a custom clear event handler
- Style the clear button
- Remove the clear button
The clear button is only visible on hover of the Input (by default).
Full code for this tutorial is in the Resources section.
Here’s a YouTube video version of this post or watch below:
In MUI, most events are available for custom handling through the component API. However, the Autocomplete API does not directly have a clear event or onClear
prop (There is a solution, keep reading).
Here’s the TypeScript error I get when I try to add onClear
:
The solution: when the clear button is clicked, the onChange
handler is fired. We can use this handler for any custom handling of the clear event.
const handleChange = (event: React.SyntheticEvent<Element, Event>, value: trailData | null) => {
console.log(value)
}
<Autocomplete onChange={handleChange} />
In the above code, I log the new value onChange
. Notice my TypeScript typing of the value prop.
Style MUI Autocomplete Clear Button
I decided the “cancel” button in the TextField Input (it only shows on hover) should also be styled blue. In the same class I added the following:
tfStyle: {
"& .MuiButtonBase-root.MuiAutocomplete-clearIndicator": {
color: "blue",
visibility: "visible"
}
}
Notice how even the indicator has a specific class applied by Material-UI since it is inside the Autocomplete.
Also, you can swap the tooltip text that appears on hover with the clearText
prop.
MUI Autocomplete Clear Icon
You can replace the clear icon with a custom icon by using the clearIcon
prop.
Here’s example code:
<Autocomplete clearIcon={<HighlightOffIcon/>} />
Notice that the clearIcon
prop requires a value of type node
.
Remove MUI Autocomplete Clear Button
The ‘Clear’ Button can be removed by using the disableClearable
prop:
<Autocomplete disableClearable />
With this prop enabled, the clear button is no longer rendered as part of the Autocomplete Input’s endAdornment
element.
You might want to disable the clear button if you enable the clearOnEscape
prop. This will clear a value that was selected while the Autocomplete is in a focus state. It will not clear a value that was previously selected.
Adding a clear button to the Select component is more challenging.
Resources
You can use MUI’s ClickAwayListener to create your own Autocomplete.
MUI Autocomplete API Docs
import Autocomplete from "@mui/material/Autocomplete";
import TextField from "@mui/material/TextField";
import HighlightOffIcon from "@mui/icons-material/HighlightOff";
interface trailData {
name: string;
state: string;
}
const tfStyle = {
"& .MuiButtonBase-root.MuiAutocomplete-clearIndicator": {
color: "blue",
visibility: "visible",
},
};
const handleChange = (
event: React.SyntheticEvent<Element, Event>,
value: trailData | null
) => {
console.log(value);
};
export default function ClearAutocomplete() {
return (
<Autocomplete
onChange={handleChange}
options={myTrails}
//disableClearable
//clearOnEscape
//clearIcon={<HighlightOffIcon/>}
id="autocomplete-1"
getOptionLabel={(option) => `${option.name}: ${option.state}`}
renderInput={(params) => {
return (
<TextField
{...params}
variant="outlined"
label="Name: State"
sx={tfStyle}
/>
);
}}
sx={{ mb: 2, minWidth: 400 }}
/>
);
}
const myTrails = [
{
name: "Eagle Mountain Lake",
state: "Texas",
},
{
name: "Inks Lake Woodland Trail",
state: "Texas",
},
{
name: "Tanyard Creek Loop",
state: "Arkansas",
},
{
name: "Turner Falls Park",
state: "Oklahoma",
},
];
Table of Contents
- Introduction
- What is Material UI Autocomplete Component?
- Setting up a React App
- Installing Material UI Library
- How does Autocomplete work in Material UI?
- Rendering Material UI Autocomplete Component
- Rendering Material UI Autocomplete Component As Normal Text Input
- Material UI Autocomplete Free Solo Prop
- Getting Multiple Values from Material UI Autocomplete Component
- Getting the Selected Value from the Material UI Autocomplete Component
- Getting Multiple Selected values from the Material UI Autocomplete
- Setting Default Multiple Selected Values in the Material UI Autocomplete
- Rendering Array Of Objects in Material UI Autocomplete Component
- Wrapping Up
- Interesting Reads From Our Blogs
Do you want to be more efficient? Consider CopyCat, an AI-powered plugin that will convert your Figma design to UI code, allowing you to concentrate on the app’s business logic.
Introduction
According to Wikipedia: Autocomplete speeds up human-computer interactions when it correctly predicts the word a user intends to enter after only a few characters have been typed into a text input field.
In this article, we’ll look at how to use the Material UI autocomplete component. Speed up user interaction with your web application by incorporating an autocomplete component into your form select element, which leads to better search results.
What is Material UI Autocomplete Component?
Material UI Autocomplete component is a great way to implement an autocomplete feature in React and according to the MUI documentation. “It is a normal text input enhanced by a panel of suggested options.”
This component provides predictive search result that makes it possible for the user to know if there are related result for their keyword even before they complete the word or sentence they’re typing.
Below is a demo of the Material UI Autocomplete component in action:
Setting up a React App
Use the following command to create a new react application:
npx create-react-app my-app
Next, run the following command to navigate to your newly generated app directory:
Then run the following command to start your React server:
Or using npm:
Your server should launch in your browser at http://localhost:3000, as shown below:
Installing Material UI Library
In this section, we’ll install the Material UI library. Run the command below to install Material UI to your project:
npm:
npm install @mui/material @emotion/react @emotion/styled
yarn:
yarn add @mui/material @emotion/react @emotion/styled
How does Autocomplete work in Material UI?
Using the Autocomplete component is relatively simple. You’ll need to provide the Autocomplete component with a list of options and a callback function that will handle the selection when the input changes.
Rendering Material UI Autocomplete Component
Given that our task is to display an array of pets in a searchable dropdown list from which users can choose their favorite pet.
const pets = ["Cat", "Dog", "Bird", "Pigeon"];
To start;
- Create a new component folder inside the src folder.
- Next, create a new PetsList.jsx file inside the component folder.
Copy and paste the code below inside the PetsList.jsx file:
import { Autocomplete, TextField } from "@mui/material"; import React from "react"; export const PetLists = () => { const pets = ["Cat", "Dog", "Bird", "Pigeon"]; return ( <Autocomplete style={{ width: "20%" }} options={pets} renderInput={(params) => ( <TextField label='Select your favourite pet' {...params} /> )} ></Autocomplete> ); };
ThePetLists component declares a constant variable called pets and sets it equal to an array of four pets names.
Next, we render the Autocomplete component with an optional style of 20% width of it’s parent element, a required options prop set to the pets array we created, this will be the dropdown options, and a renderInput prop that returns another MUI component called TextField with a label and params rest operator.
Finally, render the PetsList component in your App.js file as shown below:
import React from "react"; import { Grid } from "@mui/material"; import { PetLists } from "./components/PetLists"; function App() { return ( <React.Fragment> <Grid container direction='column' alignItems='center' justifyContent='center' style={{ minHeight: "100vh" }} > <PetLists /> </Grid> </React.Fragment> ); } export default App;
We use the MUI Grid component to center the PetLists component horizontally and vertically. More information on the MUI grid component is here.
When you visit your browser, the PetLists component should look something like this:
Cool 😎, right!
Rendering Material UI Autocomplete Component As Normal Text Input
We can render customized components or elements such as the native HTML input tag in the renderInput prop instead of the MUI TextField component as shown below:
import { Autocomplete } from "@mui/material"; import React from "react"; export const PetLists = () => { const pets = ["Cat", "Dog", "Bird", "Pigeon"]; return ( <Autocomplete style={{ width: "20%" }} options={pets} renderInput={(params) => { return ( <div ref={params.InputProps.ref}> <label for='pets-lists'>Select your favourite pet:</label> <input id='pets-lists' type='text' placeholder='Start typing...' style={{ border: "1px solid #cccccc", padding: "10px", width: "100%", }} {...params.inputProps} /> </div> ); }} ></Autocomplete> ); };
It is important to pass the ref to the parent container of the custom component or element and also pass or spread the …params.inputProps to the element in order to be controlled by MUI.
The above Autocomplete component with the native input tag will look and function something like this:
Material UI Autocomplete Free Solo Prop
Pass the freesolo prop to the Autocomplete component if you wish to render the Material UI Autocomplete component to accept undetermined values, the freesolo prop is declared in the Autocomplete component as follows:
import { Autocomplete, TextField } from "@mui/material"; import React from "react"; export const PetLists = () => { const pets = ["Cat", "Dog", "Bird", "Pigeon"]; return ( <Autocomplete freesolo style={{ width: "20%" }} options={pets} renderInput={(params) => { return <TextField label='Select your favourite pet' {...params} /> }} ></Autocomplete> ); };
The primary aim of this prop is to provide search suggestions for inputs like Google’s search engine that usually contains an arbitrary values, similar or previous searches from a predefined set of values.
Getting Multiple Values from Material UI Autocomplete Component
Pass the multiple prop to the Autocomplete component to enable multi-selection in the dropdown.
import { Autocomplete, TextField } from "@mui/material"; import React from "react"; export const PetLists = () => { const pets = ["Cat", "Dog", "Bird", "Pigeon"]; return ( <Autocomplete multiple style={{ width: "20%" }} options={pets} renderInput={(params) => { return <TextField label='Select your favourite pet' {...params} />; }} ></Autocomplete> ); };
This way, the user will be able to choose as many options as they want from the available sets of options. The MUI multi-select with autocomplete will look and function like below:
Getting the Selected Value from the Material UI Autocomplete Component
The autocomplete component provides us with some useful props to control the input value and the selected options in the Autocomplete component.
- The value prop: To control the option selected by the user.
- The onChange prop: To trigger an action when a new option is selected.
- The inputValue prop: To control and access the keyword entered by the user.
- The onInputChange prop: To trigger an action each time the user types a keyword in the search input.
We’ll make use of the useState hook to keep track of the selected option (value) from the autocomplete component, we’ll also console.log and render the selectedPet in the browser, so we can see the changes in real time.
import { Autocomplete, TextField } from "@mui/material"; import React, { useState } from "react"; export const PetLists = () => { const pets = ["Cat", "Dog", "Bird", "Pigeon"]; const [selectedPet, setSelectedPet] = useState(null); const [petInputValue, setPetInputValue] = useState(""); console.log(selectedPet); return ( <React.Fragment> <h5 style={{ marginBottom: "1rem", textAlign: "left" }}> You selected:{" "} <span style={{ color: "dodgerblue", fontWeight: "800" }}> {selectedPet} </span> </h5> <Autocomplete style={{ width: "40%" }} options={pets} value={selectedPet} onChange={(event, newPet) => { setSelectedPet(newPet); }} inputValue={petInputValue} onInputChange={(event, newPetInputValue) => { setPetInputValue(newPetInputValue); }} renderInput={(params) => { return <TextField label='Select your favourite pet' {...params} />; }} ></Autocomplete> </React.Fragment> ); };
The above code will function as demonstrated below:
As demonstrated above, the option selected is captured and rendered in both dom and the browser console.
Getting Multiple Selected values from the Material UI Autocomplete
For the multiple selected values, the initial value for the useState will be an empty array and we’ll remove the value prop from the Autocomplete component, MUI will handle the value for us once we pass the multiple prop in the Autocomplete component.
We’ll also map the selectedPets to the UI to see the selected options in realtime.
import { Autocomplete, TextField } from "@mui/material"; import React, { useState } from "react"; export const PetLists = () => { const pets = ["Cat", "Dog", "Bird", "Pigeon"]; const [selectedPets, setSelectedPets] = useState([]); const [petInputValue, setPetInputValue] = useState(""); console.log(selectedPets); return ( <React.Fragment> <h5 style={{ marginBottom: "1rem", textAlign: "left" }}> You selected:{" "} <span style={{ color: "dodgerblue", fontWeight: "800" }}> {selectedPets .map((pet, i, arr) => arr.length > 1 && arr.length - 1 === i ? ` and ${pet}.` : pet ) .join(", ") || "Nothing yet"} </span> </h5> <Autocomplete multiple style={{ width: "40%" }} options={pets} onChange={(event, newPet) => { setSelectedPets(newPet); }} inputValue={petInputValue} onInputChange={(event, newPetInputValue) => { setPetInputValue(newPetInputValue); }} renderInput={(params) => { return <TextField label='Select your favourite pets' {...params} />; }} ></Autocomplete> </React.Fragment> ); };
The above component will function as follows:
As demonstrated above, the selected options are added to the list within the input and are removable through the close icons.
Setting Default Multiple Selected Values in the Material UI Autocomplete
You can set a default value for the multiple select option in the useState and defaultValue prop in the Autocomplete component as follows:
import { Autocomplete, TextField } from "@mui/material"; import React, { useState } from "react"; export const PetLists = () => { const pets = ["Cat", "Dog", "Bird", "Pigeon"]; const [selectedPets, setSelectedPets] = useState([pets[2], pets[3]]); const [petInputValue, setPetInputValue] = useState(""); console.log(selectedPets); return ( <React.Fragment> <h5 style={{ marginBottom: "1rem", textAlign: "left" }}> You selected:{" "} <span style={{ color: "dodgerblue", fontWeight: "800" }}> {selectedPets .map((pet, i, arr) => arr.length > 1 && arr.length - 1 === i ? ` and ${pet}.` : pet ) .join(", ") || "Nothing yet"} </span> </h5> <Autocomplete multiple defaultValue={selectedPets} style={{ width: "40%" }} options={pets} onChange={(event, newPet) => { setSelectedPets(newPet); }} inputValue={petInputValue} onInputChange={(event, newPetInputValue) => { setPetInputValue(newPetInputValue); }} renderInput={(params) => { return <TextField label='Select your favourite pets' {...params} />; }} ></Autocomplete> </React.Fragment> ); };
Note that the default value must be an array when using the multiple prop.
Our default multiple selected options rendered shown below:
Rendering Array Of Objects in Material UI Autocomplete Component
Given that you’re to render an array of pets objects with name and img properties as structured below:
const pets = [ { name: "Cats", img: "🐈" }, { name: "Dog", img: "🦮" }, { name: "Bird", img: "🦜" }, ];
We’ll make use of the autocomplete getOptionLabel prop to specify the key in the object that we want to render as the option.
Below will be our getOptionLabel given that we want to use the name key as our option:
getOptionLabel={(option) => option.name}
We can also customize the dropdown option label through the renderOption prop as shown below:
renderOption={(props, option) => ( <Box component='li' {...props}> {option.name} {option.img} </Box> )}
This will render the name and image of the pet objects side by side in the dropdown options like this Cat 🐈.
Putting everything together, our component will look like this:
import { Autocomplete, TextField } from "@mui/material"; import { Box } from "@mui/system"; import React, { useState } from "react"; export const PetLists = () => { const pets = [ { name: "Cats", img: "🐈" }, { name: "Dog", img: "🦮" }, { name: "Bird", img: "🦜" }, ]; const [selectedPet, setSelectedPet] = useState(null); const [petInputValue, setPetInputValue] = useState(""); console.log(selectedPet); return ( <React.Fragment> <h5 style={{ marginBottom: "1rem", textAlign: "left" }}> You selected:{" "} <span style={{ color: "dodgerblue", fontWeight: "800" }}> {selectedPet ? `${selectedPet?.name} ${selectedPet?.img}` : "Nothing yet"} </span> </h5> <Autocomplete style={{ width: "40%" }} options={pets} getOptionLabel={(option) => option.name} renderOption={(props, option) => ( <Box component='li' {...props}> {option.name} {option.img} </Box> )} onChange={(event, newPet) => { setSelectedPet(newPet); }} inputValue={petInputValue} onInputChange={(event, newPetInputValue) => { setPetInputValue(newPetInputValue); }} renderInput={(params) => { return <TextField label='Select your favourite pet' {...params} />; }} ></Autocomplete> </React.Fragment> ); };
The above complete component will function as shown below:
As demonstrated above, the selectedPet will return an object of the selected pet from the dropdown and we render the name and img in the dom and the whole object in the browser’s console.
Wrapping Up
In this article, we discussed how to use the React Material UI Autocomplete to display the possible values in a search component. You’ve also learned how to create an MUI autocomplete component, set its default value, and retrieve the component’s selected values.
We hope you found this article useful in understanding the React Material UI Autocomplete and how to use it in your own applications.
You can also learn how to customize and render asynchronous request data as options in the Material UI Autocomplete component from the video below:
Finally, more articles like this are on our CopyCat blog. CopyCat converts your Figma files into a ready-to-use React project, saving you over 35% of development time. You can check out CopyCat here.
Interesting Reads From Our Blogs
- Creating Tooltips in React with React-Tooltip Library
- A Guide to Understanding Tricky React setState in Simple Terms
- Top 17 VSCode Plugins to Boost Productivity as a Developer
Happy Coding 👨🏽💻
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and
privacy statement. We’ll occasionally send you account related emails.
Already on GitHub?
Sign in
to your account
Open
nihil-pro opened this issue
Nov 17, 2021
· 23 comments
Comments
Duplicates
- I have searched the existing issues
Latest version
- I have tested the latest version
Current behavior 😯
I get an «value provided to Autocomplete is invalid» error:
None of the options match with `{"id":18877,"YearFrom":2002,"YearTo":2005,"Make":"Hyundai","Model":"Getz","Generation":"I (2002—2005)","FuelType":"Бензин","DriveType":"Передний","Transmission":"Автомат","Modification":"1.4 AT (97 л.с.)","Power":97,"EngineSize":"1.4","BodyType":"Хетчбэк","Doors":"5"}`.
You can use the `isOptionEqualToValue` prop to customize the equality test.
Expected behavior 🤔
Well, if i use the isOptionEqualToValue={(option, value) => option.id === value.id}
, the expected behavior is that I shouldn’t get an error.
Steps to reproduce 🕹
Steps:
- Use Autocomplete:
<Autocomplete
multiple
fullWidth
filterSelectedOptions
defaultValue={defaultValues}
options={options}
isOptionEqualToValue={(option, value) => option.id === value.id}
getOptionLabel={(option: ICar) =>
`(${option.id})
${option.Make} ${option.Model} ${option.Generation}
${option.EngineSize} л ${option.FuelType?.toLowerCase()} ${option.Power} л.с. – ${option.Transmission?.toLowerCase()} ${option.DriveType?.toLowerCase()} привод
`
}
renderInput={(params: AutocompleteRenderInputParams) => <TextField {...params} label={'Применяемость'} />}
renderOption={(props, option: ICar) => {
return (
<li {...props}>
{option.Make} {option.Model} {option.Generation} {option.EngineSize} л {option.FuelType?.toLowerCase()} {option.Power} л.с. – {option.Transmission?.toLowerCase()} {option.DriveType?.toLowerCase()} привод
</li>
)
}}
onChange={(_, value) => onChange(value)}
onInputChange={(event, value) => {
if (value && value.length >= 3) {
search(value).finally()
}
}}
/>
- The defaultValue is also a type Array
- I get an error with defaulValue options only
Context 🔦
No response
Your environment 🌎
`npx @mui/envinfo`
System:
OS: macOS 12.0.1
Binaries:
Node: 16.9.1 - /usr/local/bin/node
Yarn: 1.22.4 - /usr/local/bin/yarn
npm: 6.14.6 - ~/node_modules/.bin/npm
Browsers:
Chrome: 96.0.4664.45
Edge: Not Found
Firefox: 94.0.1
Safari: 15.1
npmPackages:
@emotion/react: ^11.4.1 => 11.4.1
@emotion/styled: ^11.3.0 => 11.3.0
@mui/core: 5.0.0-alpha.50
@mui/icons-material: ^5.0.3 => 5.0.3
@mui/material: ^5.0.3 => 5.0.3
@mui/private-theming: 5.0.1
@mui/styled-engine: 5.0.1
@mui/styles: 5.0.1
@mui/system: 5.0.3
@mui/types: 7.0.0
@mui/utils: 5.0.1
@mui/x-data-grid: ^5.0.0-beta.3 => 5.0.0-beta.3
@types/react: ^17.0.20 => 17.0.20
react: ^17.0.2 => 17.0.2
react-dom: ^17.0.2 => 17.0.2
typescript: ^4.4.3 => 4.4.3
michaldudak
changed the title
The value provided to Autocomplete is invalid. (with the installed isOptionEqualToValue)
[Autocomplete] The value provided to Autocomplete is invalid. (with the installed isOptionEqualToValue)
Nov 17, 2021
Sorry for the late response. I’m getting a 404 error on the sandbox you provided. Could you check it?
I have this issue as well, and I think its the same case. It can happen when implementing an external async search. The Autocomplete component still has a value, but the options are now populated with the results from the search, which does not include the current value.
Here is a reproduction: https://codesandbox.io/s/asynchronous-material-demo-forked-q0yox?file=/demo.tsx
Steps to repro:
- type «inter» in the Autocomplete, chose «Interstellar»
- deselect the Autocomplete
- select the Autocomplete, type «godfather»
- observe console
It doesn’t repro 100% of the time, but its pretty easy to get it to happen.
Thanks for the reproduction, @markedwards. I’m reopening the issue.
I’m experiencing this problem too. Great to see that this issue is opened!
Also ran into this issue while fetching autocomplete options from the Google Maps Places API and my own backend. I tried to work around the issue by clearing out the value
prop when the inputValue
changed since any change in inputValue
changes the options
prop with new API results. This makes the warning go away but results in a clunky user behavior where it takes 2 keystrokes to start a new «search» instead of one. For some reason, the first one keystroke disappears into oblivion. (E.g. if you highlight the text and then try to replace it by typing, your first key stroke only clears the text and doesn’t place any actual letters into the TextField
.) It sounds like some order of operations problem so I didn’t poke in further.
Open to suggestions for other ways to avoid the warning or if it’s okay to just to ignore it overall.
Could we please get an option to turn off the warning completely? At this stage it’s causing a lot of noise in tests and, as reported, it’s not warning anything useful.
I have the same Issue … boring warning
Apparently, at this point, the warning causes more confusion than assistance. If anyone is willing to investigate if this warning can be disabled for cases of async loading without much hassle, then feel free to grab this issue.
I’m also considering removing the warning altogether, but let’s try the less destructive option first.
colinscruggs, ariel-upstream, and Victor-Villacis reacted with hooray emoji
In case anyone stumbles upon this issue today still, the trick for async-populated options is to add the currently selected option so that it doesn’t trigger the false positive that can happen when setting a new value while an async request changes the array of options that is provided.
const [item, setItem] = useState<ItemModel>(); const [items, setItems] = useState<ItemModel[]>([]); const [input, setInput] = useState(''); const query = useDebounce(input, 250); useEffect(() => { fetchItems(query).then((items) => setItems(items)); }, [query]); return ( <Autocomplete autoComplete filterSelectedOptions getOptionLabel={(option) => option.name} onInputChange={(_event, value) => setInput(value)} onChange={(_event, value) => setItem(value)} options={item ? [item, ...items] : items} renderInput={(extra) => <TextField {...extra} label="Name" />} /> );
still an issue for me. not wanting to add more state as said in @angrybacon answer, because i use react hook form mainly to control the state.
<Controller name={"district"} control={control} render={({ field: { onChange, value } }) => ( <Autocomplete onChange={(e, data) => onChange(data)} options={districts.map(({ id, name }) => ({ id, name }))} value={value} getOptionLabel={(option) => option.name} isOptionEqualToValue={(option, newValue) => { return option.id === newValue.id; }} renderInput={(params) => ( <TextField {...params} placeholder="test" label="test" /> )} onInputChange={(_evt, newValue) => setSearch(newValue)} inputValue={search} /> )} />
can i just return true for isOptionEqualToValue
prop? is it affect anything., didn’t find the effect so far. even still don’t get it the purpose of that prop in the first place
I’m currently struggling with this. I’m going to use this comment box as a rubber duck and maybe someone can explain what I’m doing wrong.
So… This error has made me realize I don’t entirely get what the point of the value
field is anyway. As I understand it, the autocomplete component is essentially a text input and a select welded together. The onChange
handler fires when the user clicks an item à la select, whereas onInputChange
and inputValue
are for the text input portion.
From what I can see, the both the text input and the selection set the value
, which makes sense because while there’s only one value for the component, even if there’s two ways to set it.
I’ve been storing my value in a hook, and passing it back in to both value
and inputValue
and getting this warning (even though everything works fine). I figured it’s because value
expects some item in my options
array (such as an {label: string; id: string;}
). So I tried storing the value
separately (as the docs do indeed suggest), but that:
- Does not suppress the warning (it just complains now that the object doesn’t match either).
- Causes the typed input to be over-written by the last select value.
The latter I suppose can be mitigated by unsetting the «select value» when the user modifies the input text and falling back to value={inputValue}
but that raises the question, «Why set it to anything besides inputValue
in the first place?»
So, am I doing something wrong or is this warning truly just meaningless? I feel like I must be doing something wrong because surely the value
prop is not as useless as it appears to be?
I think for now I will just use a <Select />
instead of <Autocomplete />
. I’ll miss the nice filtering/instant search but I don’t really want a component like this where I can’t figure out from the documentation how it’s supposed to be used and which spews out console warnings in my production code base. 😞
As a closing note, if my intuitions are correct, I’d argue the naming of the Autocomplete
props is rather misleading/confusing. The text field value should be called value
and have its onChange
property, and there should be another prop called onSelect
or ‘onComplete` which is used for when the value is set by via autocomplete behavior.
Thoughts?
I forked the controlled input example from the documentation and tweaked it slightly, working towards what I was actually building (albeit not for Harry Potter characters).
Although the warning does not show up, there is a lot of questionable UX going on. You will notice the behavior where you delete half the name and it reappears on blur because of the odd implementation choice of having the Autocomplete
have two values.
I’ve «solved» that by setting value
via the onInputChange
handler, which removes the need for two pieces of state, and removes the weird ambiguity between the two competing/conflicting values. But the documentation explicitly advises against this.
Of course, creating this dummy value object does make it clear why the warning shows up.
Which would bring me back to: Either the Autocomplete component should be completely re-thought (probably the «right» solution) w.r.t. multiple values and setters, OR the warning should be ditched and you can set the value to whatever you want.
The latter also does feel like a «right» choice considering entering non-specific options is kind of the whole thing that sets <Autocomplete />
apart from <Select />
. If you don’t want the user to be able to type in random other text, use a <Select />
, no?
Got same issue. Too bad there’s no solution yet, but glad that it’s on the radar.
I had a bit different kind of a problem that is described in this thread, but as it lands on same field I decided to post here rather to post new thread (hope that’s fine).
TL;DR
A opinion that has very minor affect on… anything. May resolve some common issues found in internet with this component.
Decription
I have MUIv5 and I use MUI Autocomplete, with Formik (with Yup) and use typescript. I’ve come into an opinion where I feel like that useAutocomplete (@mui/base/AutocompleteUnstyled/useAutocomplete.js
) applies bad practices.
By the snipped code below, it states that the Autocomplete initial value can not be undefined
, but rather null
?
— henche, to my knowledge undefined
is stated as «not set» or in validation schemes «not touched» and null
is actually a defined value («someone or something have changed or set it’s value to null).
In my case this caused the form to be initialized with «undefined»… string (what the …) and bunch of error/warning messages, saying:
Material-UI: the value provided to Autocomplete is invalid. None of the options match with ´""´.
The getOptionLabel method of Autocomplete returned undefined instead of a string for ""
.
if (process.env.NODE_ENV !== 'production') {
if (value !== null && !freeSolo && options.length > 0) { // <-----------
const missingValue = (multiple ? value : [value]).filter(value2 => !options.some(option => isOptionEqualToValue(option, value2)));
if (missingValue.length > 0) {
console.warn([`MUI: The value provided to ${componentName} is invalid.`, `None of the options match with `${missingValue.length > 1 ? JSON.stringify(missingValue) : JSON.stringify(missingValue[0])}`.`, 'You can use the `isOptionEqualToValue` prop to customize the equality test.'].join('n'));
}
}
}
Curtain call
I resolved my problem by initializing form values with null and applying nullable
into Yup validation schema for the fields.
Sample code:
<Field
name="fieldName"
component={Autocomplete}
options={sortedfieldNameKeys}
// getOptionLabel={(option: string) => sortedfieldNameKeys?.length > 0 ? fields[option] : "" }
getOptionLabel={(option: string) => sortedfieldNameKeys?.length > 0 ? fields[option] : null }
renderInput={(params: AutocompleteRenderInputParams) => (
<MuiTextField
{...params}
name="fieldName"
label={translator('fieldName')}
variant="standard"
helperText={touched?.fieldName && errors.fieldName && errors.fieldName}
error={touched?.fieldName && Boolean(errors.fieldName)}
/>
)}
sx={{ width: '20ch', mr: 4 }}
/>
// ....
=> fields = await getFields(); // [ { "01aeed68-...", value: "Field 1" }, { "470401eb-...", value: "Field 2" },... ]
// ....
=> sortedFieldNameKeys = Object.entries(fields).map(({key, value}) => (key));
// ....
=> form = {
...,
fieldName: null, // '' // undefined ... oof
}
Is there any update on this? Still able to reproduce this issue locally. @michaldudak
I’m facing this warning everytime when I try to select a link, that I’ve provided in dropdown, with the help of paper component.
I’m sharing the Autocomplete component and the UI screenshot of the dropdown
<Autocomplete popupIcon={''} fullWidth clearOnBlur clearOnEscape sx={[ error.fullAddress ? commonClasses.fieldError : {}, onboardingStyle.searchAddressAutocomplete, ]} loading={isOnboardingFormAddressLookUpSearchLoaderActive} disableClearable={!!searchAddressValue} value={onboardingForm.fullAddress} data-testid={ON_BOARDING_TEST_CONSTANTS.ADDRESS} options={searchAddressResults} PaperComponent={(paperComponentProps) => ( <SearchAddressDropdown paperComponentProps={paperComponentProps} onboardingForm={onboardingForm} setOnboardingForm={setOnboardingForm} setIsAddressModalOpen={setIsAddressModalOpen} /> )} noOptionsText={ searchAddressValue?.length > 2 ? 'No search results found' : 'Search address..' } renderInput={(params) => ( <TextField onChange={debounceSearch} {...params} label='ADDRESS' InputProps={{ ...params.InputProps, type: 'search', }} helperText={error?.fullAddress} /> )} onChange={onOptionChange} />
For anyone who may still be running into this warning. The issue I ran into was a mislabelling of «freeSolo», with a capital «S».
I have got the same issue — getting the warning: «The value provided to Autocomplete is invalid. None of the options match with «». You can use the isOptionEqualToValue
prop to customize the equality test».
Adding the final part || value === ""
to isOptionEqualToValue={(option, value) => option.name === value.name || value === ""
removes the warning, but then a new problem appears: all options turn colored as if all were chosen
I have multi select AutoComplete
. This is the way I solve the problem:
- options: <[{ label, value, type }]>
- Turn on
freeSolo
,autoSelect
to ignore warning - Turn on
clearOnBlur
to ignore value that not [Enter] or throw selecting an option in dropdown - Modify
onChange
to handle unwanted text
<Autocomplete
freeSolo
disabled={loading}
multiple
clearOnBlur
options={options}
getOptionLabel={(option) => option.label}
groupBy={(option) => option.type}
/** Help show no option dropdown, freeSolo does not show it **/
getOptionDisabled={(option) => option.label && option.label.toLowerCase() === 'no option'}
filterOptions={() => {
if (!options.length) return [{ label: 'No option', value: 'No option' }];
return options;
}}
value={selectedOptions}
onChange={(event, selectedValues) => {
// This only apply to multi select
if (['removeOption', 'clear'].includes(reason)) {
setSelectedOptions(selectedValues);
}
if (['selectOption'].includes(reason)) {
const lastValue = [...multiValue].pop();
const isAllow = options.some((item) => item.label === lastValue?.label);
if (!isAllow) break;
setSelectedOptions(multiValue);
}
}}
onInputChange={(event, value) => setSearchTerm(value)}
renderInput={(params) => (
<TextField {...params} label="label" />
)}
/>
Happy coding
While freesolo
may work to solve this issue, it seems like a workaround for a common use case. Using multi-select with server-side search is a common pattern that shouldn’t trigger a warning.
Perhaps there should be a prop to signify «Search as you type» (as the Docs refers to it) that does not allow for free entry but does allow for items to exist in the values that don’t exist in the current options and, by default, disables filterOptions
(instead of having to set filterOptions={(x) => x}
as the Docs recommends).