Search code examples
javascriptreactjsmaterial-uijsx

How can I add unique keys to React/Material UI Autocomplete component?


I'm trying to create a Material UI Autocomplete component that essentially just displays search results to the user. Some of the options' names will be duplicates, but they will all have unique IDs. I receive the following warning:

index.js:1 Warning: Encountered two children with the same key, Name B. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.

const SearchField = () => {
    const [open, setOpen] = React.useState(false)
    const [searchQuery, setSearchQuery] = React.useState('')
    const [searchResults, setSearchResults] = React.useState([])
    const loading = true //later

    const debounced = useDebouncedCallback(
        async searchQuery => {
            if (searchQuery) {
                let result = await doSearch(searchQuery)
                if (result.status === 200) {
                    setSearchResults(result.data)
                } else {
                    console.error(result)
                }
            }
        },
        1000
    )

    const handleInputChange = e => {
        if (e.target.value && e.target.value !== searchQuery) {
            debounced(e.target.value)
            setSearchQuery(e.target.value)
        }
    }

    const options = [{
        name: 'Name A',
        id: 'entry_0597856'
    },{
        name: 'Name B',
        id: 'entry_3049854'
    },{
        name: 'Name B',
        id: 'entry_3794654'
    },{
        name: 'Name C',
        id: 'entry_9087345'
    }]


    return (
        <Autocomplete
            id='search_freesolo'
            freeSolo
            selectOnFocus
            clearOnBlur
            handleHomeEndKeys
            autoHighlight
            onInputChange={handleInputChange}
            open={true}
            onOpen={() => setOpen(true)}
            onClose={() => setOpen(false)}
            loading={loading}
            key={option => option.id}

            options={options}
            getOptionLabel={option => option.name}

            renderOption={(props, option) => (
                <Box
                    component='li'
                    {...props}
                >
                    {option.name}
                </Box>
            )}

            renderInput={params => {
                return (
                    <TextField
                        {...params}
                        required
                        id="search_bar"
                        label="Search"
                        InputProps={{
                            ...params.InputProps,
                            endAdornment: (
                                <React.Fragment>
                                    {loading ? <CircularProgress size={18} /> : null}
                                    {params.InputProps.endAdornment}
                                </React.Fragment>
                            )
                        }}
                    />
                )}
            }
            
        />
    )
}

Solution

  • You can define your own renderOption that can return the list item with a correct key value. Your code complains about the duplicated keys because by default, Autocomplete uses the getOptionLabel(option) to retrieve the key:

    <Autocomplete
      renderOption={(props, option) => {
        return (
          <li {...props} key={option.id}>
            {option.name}
          </li>
        );
      }}
      renderInput={(params) => <TextField {...params} label="Movie" />}
    />
    

    If it still doesn't work, check your props order, you need to declare the key prop last, if you put it before the props provided by the callback:

    <Box component='li' key={key} {...props}
    

    Then it will be overridden by the props.key from MUI. It should be like this:

    <Box component='li' {...props} key={key}
    

    Live Demo

    Codesandbox Demo