ilernet2
ilernet2

Reputation: 175

React - Autocomplete calling API

I'm using the Material UI Autocomplete component to make an API call in a method that returns a JSON. The idea is that when the user types in the autocomplete component it makes a call to find the results matching the string.

The problem I have with the code I put is that I don't know how to make the API call and return the results in the autocomplete component

const [itemsAutocomplete, setItemsAutocomplete] = useState([])

 <Autocomplete
     disablePortal
     id="autocomplete-search"
     onChange={handleItemsOptions}
     getOptionLabel={option => option.name}
     sx={{ width: 300 }}
     renderInput={params => (
     <TextField {...params} label="Search an item..." />
     )}
     />



const handleItemsOptions = event => {

    const requestOptions = {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            name : search_name,
        }),
    }

   
    fetch(
        `${process.env.NEXT_PUBLIC_BACKEND_URL}/api/product/items/search/`,
        requestOptions,
    )
        .then(response => response.json())
        .then(json => setItemsAutocomplete(json))
}

Upvotes: 10

Views: 21978

Answers (1)

Ian A
Ian A

Reputation: 6128

If you want to update the options every time the user types into the search box, you could use the onInputChange property of the Autocomplete and hook that up to a function which performs the API call and updates the options based on the results.

HTML

<Autocomplete
        id="combo-box-demo"
        options={options}
        onInputChange={onInputChange}
        getOptionLabel={(option) => option.title}
        style={{ width: 300 }}
        renderInput={(params) => (
          <TextField {...params} label="Combo box" variant="outlined" />
        )}
      />

Javascript

const [options, setOptions] = useState([]);
const previousController = useRef();

const getData = (searchTerm) => {
  if (previousController.current) {
    previousController.current.abort();
  }
  const controller = new AbortController();
  const signal = controller.signal;
  previousController.current = controller;
  fetch("https://dummyjson.com/products/search?q=" + searchTerm, {
    signal,
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json"
    }
  })
    .then(function (response) {
      return response.json();
    })
    .then(function (myJson) {
      console.log(
        "search term: " + searchTerm + ", results: ",
        myJson.products
      );
      const updatedOptions = myJson.products.map((p) => {
        return { title: p.title };
      });
      setOptions(updatedOptions);
    });
};

const onInputChange = (event, value, reason) => {
  if (value) {
    getData(value);
  } else {
    setOptions([]);
  }
};

onInputChange will perform an HTTP request (getData(value)) if the user has typed something. If the string is empty (i.e. the user has deleted the text they typed) it will default the options to the empty array. In this function, value is the string the user has typed.

getData performs an HTTP request and calls setOptions to update the autocomplete options based on the results returned. The use of AbortController is to ensure that when the user types (or deletes) a character and the next HTTP request is made, previous HTTP requests are cancelled. This is because depending on the speed of the API, requests may not return in the order they are sent.

See this CodeSandbox for a working demo.

Upvotes: 17

Related Questions