ZooPanda
ZooPanda

Reputation: 331

React MUI Autocomplete "options.filter" is not a function

I wish to create a autocomplete search bar with my own custom call to the backend, which searches through a list of tickers.

<Autocomplete
                multiple
                id="checkboxes-tags-demo"
                options={watchlistSearchTickers}
                disableCloseOnSelect
                getOptionLabel={(option: any) => (option!) ? option.Symbol : null}
                renderOption={(props, option, { selected }) => (
                    <li {...props}>
                        {option.Symbol}
                    </li>
                )}
                style={{ padding: 0 }}
                onChange={(event, query: any) => handleWatchlistSearch(query)}
                filterOptions={(x) => x}
                renderInput={(params) => (
                    <div ref={params.InputProps.ref}>
                        <input type="text" {...params.inputProps} />
                    </div>
                )}
            />

The initial render here seems fine, but on click the text input box, an error "options.filter" is not a function occurs. Here is the function that calls the backend through a post request:

const [watchlistSearchTickers, setWatchlistSearchTickers] = useState<Array<watchlistSearchInterface>>([])

function handleWatchlistSearch(query: string) {
        axiosInstance.post("/portfolio/watchlist/search/", {
            query: query
        }).then((res) => {
            console.log(res)
            setWatchlistSearchTickers(res.data)
        })
    }

    useEffect(() => {
        handleWatchlistSearch("")
    }, []) // Initialize with empty list of tickers

Does anyone know why this happens?

Upvotes: 10

Views: 10977

Answers (2)

erreyon.js
erreyon.js

Reputation: 11

th3oll is correct in that Load on Open solves this nicely. Some tips:

To get Load on Open working, you basically put your state and useEffect logic into the handleOpen() function placed inside a component, as mentioned in the docs. The call in turn is the "effect" of the <Autocomplete> opening

Ex. as it worked for me:

const SomeComponent = () => {
   const [open, setOpen] = useState(false);
   const [options, setOptions] = useState([]);
   const [loading, setLoading] = useState(false);

   const handleOpen = () => {
        setOpen(true);
        const makeBackEndCall = async () => {
            setLoading(true)
            const response = await fetch(`your.endpoint.com/data`, {
                method: 'GET',
                headers: {
                    'Content-Type': 'application/json',
                    // etc
                },
            });

            setLoading(false)
            return await response.json()
        }
        makeBackEndCall().then(data => setOptions(data))
    }

    const handleClose = () => {
    //should be able to copy paste
    }

    return (
       <Autocomplete /> //copy same from docs
    )

};

Then depending on the format of your data coming back, you may have to either morph the data to fit the options structure or use isOptionEqualtoValue or getOptionsLabel props to process your data. For me, I simply ended up having to delete the getOptionsLabel prop. The docs say options prop should accept a simple array of strings out of the box, but that did not work for me.

The way the docs show the placeholder async function makes it look a bit confusing, but everything else is just copy/paste

Hope this helps! --erre

Upvotes: 1

th3oll
th3oll

Reputation: 106

So this bug actually happens when you pass null or undefined values into the options prop of the Autocomplete component. This is likely happening because you're clicking in the Autocomplete "too fast", so before it can get the results from your API call, it passes a value that the component tries to .filter() it, causing the error.

I was also having this issue, and I managed to figure a workaround that works (it's not perfect but it stops your code from crashing).

<Autocomplete
            multiple
            id="checkboxes-tags-demo"
            options={!watchlistSearchTickers ? [{label:"Loading...", id:0}] : watchlistSearchTickers }
(...)

With this ternary, what we do is that while your API is working on your request, it puts a placeholder "Loading..." option so it won't crash your code (Similar-ish to the Load on open prop, but I never figured how to make that work).

For me the solution above works well, but the fanciest and best choice would be the Load prop that is something from MUI.

Hope I could help!

Edit: Actually, if you can initialize your options to an empty array [], you can set the loading prop as true, which will display loading while your content loads!

Upvotes: 5

Related Questions