How to handle asynchronous requests in autocomplete in Material UI?


Modern web applications frequently use autocomplete components, which let users search for and choose options from a list of options as they type. The popular UI framework Material UI provides a robust Autocomplete component that enhances the user experience. Efficiently handling requests becomes crucial in scenarios where autocomplete data is fetched asynchronously.

This article will explore how to handle asynchronous requests in Autocomplete using Material UI to ensure a seamless user experience.

What are Asynchronous Requests?

In subsequent connections, asynchronous responses are sent back to the client, allowing it to send new requests without having to block while awaiting the response. The gateway always sends synchronous responses by default. A few extra steps are necessary if you want asynchronous responses.

In Autocomplete, asynchronous requests are crucial when working with large datasets or obtaining information from a third−party API. The asynchronous approach ensures that the user interface is responsive throughout the data retrieval procedure, avoiding any application lag or freezing.

Asynchronous requests in Autocomplete

The autocomplete can handle asynchronous requests with two different use cases, which are discussed below −

  • Load on open − The first use case is a load on open, which displays a progress state as long as the network request is pending. It awaits interaction with the component before loading the options.

  • Search as you type − The second use case involves searching as you type, where a new request is sent with each keypress. Consider throttling requests if your logic involves retrieving new options with each keypress and using the textbox's current value to filter the server.

    The Autocomplete component's built−in filtering must also be turned off by overriding the filterOptions prop −

<Autocomplete filterOptions={(x) => x} />

Now let’s move further and see the complete steps top handle the asynchronous requests in autocomplete component in Material UI react.

Steps to handle asynchronous requests in autocomplete

Step 1: create a new react app project

For every autocomplete component to work, we need a react application to work on. To create a new, react app, follow the below command −

npx create-react-app mynewproject
cd mynewproject

Step 2: Install Material UI and its required modules

After creating the react application, let’s now install material ui and its required dependencies using below command −

npm install @mui/material @emotion/react @emotion/styled

Step 3: Import the autocomplete

To import the components in the react main component as shown below −

import React from 'react';
import TextField from '@mui/material/TextField';
import Autocomplete from '@mui/material/Autocomplete';

Step 4: adding delay function

To handle the asynchronous request, we’ll first create a delay sleeping function that returns a Promise that resolves after the specified time.

function delSleep(delay = 0) {
   return new Promise((resolve) => {
      setTimeout(resolve, delay);
   });
}

Step 5: Controlling autocomplete

const [autoOpen, setAutoOpen] = useState(false);
const [opData, setOpData] = useState([]);
const load = open && options.length === 0;

//the below fetches options only when Autocomplete is open
useEffect(() => {
   let active = true;

   if (!load) {
      return undefined;
   }

   (async () => {
      await sleep(1e3);

      if (active) {
         setOpData([...yourData]);
      }
   })();

   return () => {
      active = false;
   };
}, [load]);

Step 6: Rendering async requests in autocomplete

Below is the code for rendering the asynchronous requests using the defined variables and useEffect −

<Autocomplete
   open={autoOpen}
   onOpen={() => {
      setAutoOpen(true);
   }}
   onClose={() => {
      setAutoOpen(false);
   }}
   isOptionEqualToValue={(opData, value) => opData.label === value.label}
   getOptionLabel={(opData) => opData.label}
   options={opData}
   loading={load}
   renderInput={(params) => (
      <TextField
         InputProps={{
            ...params.InputProps,
            endAdornment: (
               <div>
                  {load ? <CircularProgress  /> : null}
                  {params.InputProps.endAdornment}
               </div>
            ),
         }}
      />
   )}
/>

Example 1

The below example demonstrates the usage of handling asynchronous requests in autocomplete. Here, when the user opens the autocomplete, a progress state is displayed as long as the network request is pending to display the data or available options from the array of data lists.

import React, { useEffect, useState } from "react";
import TextField from "@mui/material/TextField";
import Autocomplete from "@mui/material/Autocomplete";
import CircularProgress from "@mui/material/CircularProgress";

const data = [
   { label: "Java language" },
   { label: "Python language" },
   { label: "C++ language" },
   { label: "C language" },
   { label: "Go language" },
   { label: "JavaScript language" },
   { label: "SQL" },
   { label: "MySQL" },
   { label: "HTML" },
   { label: "CSS" },
   { label: "Nextjs " },
   { label: "ReactJS " },
   { label: "VueJS " },
   { label: "Angular " },
   { label: "Angular JS " },
   { label: "PHP language" },
   { label: "R language" },
   { label: "Objective C language" },
   { label: "Cobol language" },
   { label: "Perl language" },
   { label: "Pascal language" },
   { label: "LISP language" },
   { label: "Fortran language" },
   { label: "Swift language" },
   { label: "Ruby language" },
   { label: "Algol language" },
   { label: "Scala language" },
   { label: "Rust language" },
   { label: "TypeScript language" },
   { label: "Dart language" },
   { label: "Matlab language" },
   { label: "Ada language" },
   { label: ".NET language" },
   { label: "Bash language" },
];

function delaySleep(delay = 0) {
   return new Promise((resolve) => {
      setTimeout(resolve, delay);
   });
}

export default function App() {
   const [open, setOpen] = useState(false);
   const [dataOptions, setDataOptions] = useState([]);
   const load = open && dataOptions.length === 0;

   useEffect(() => {
      let active = true;

      if (!load) {
         return undefined;
      }

      (async () => {
         await delaySleep(1e3); // For demo purposes.

         if (active) {
            setDataOptions([...data]);
         }
      })();

      return () => {
         active = false;
      };
   }, [load]);

   useEffect(() => {
      if (!open) {
         setDataOptions([]);
      }
   }, [open]);

   return (
      <div
         style={{
            display: "flex",
            marginTop: 30,
            flexDirection: "column",
            alignItems: "center",
            justifyContent: "center",
         }}>
         <Autocomplete
            sx={{ width: 400 }}
            open={open}
            onOpen={() => {
               setOpen(true);
            }}
            onClose={() => {
               setOpen(false);
            }}
            getOptionLabel={(option) => option.label}
            isOptionEqualToValue={(option, value) => option.label === value.label}
            loading={load}
            options={dataOptions}
            renderInput={(params) => ( //render the Textfield with the data options
               <TextField
                  {...params}
                  label="Search your favourite programming language"
                  InputProps={{
                     ...params.InputProps,
                     endAdornment: (
                        <div>
                           {load ? (
                              <CircularProgress color="primary" size={30} />
                           ) : null}
                           {params.InputProps.endAdornment}
                        </div>
                     ),
                  }}
               />
            )}
         />
      </div>
   );
}

Output

Example 2

The below example demonstrates the use of handling asynchronous requests in autocomplete. Here, when the user types any keyword, it fetches new options on each keystroke and uses the current value of the textbox to filter on the server.

import React, { useEffect, useState } from "react";
import TextField from "@mui/material/TextField";
import Autocomplete from "@mui/material/Autocomplete";
import CircularProgress from "@mui/material/CircularProgress";

const data = [
   { label: "Java language" },
   { label: "Python language" },
   { label: "C++ language" },
   { label: "C language" },
   { label: "Go language" },
   { label: "JavaScript language" },
   { label: "SQL" },
   { label: "MySQL" },
   { label: "HTML" },
   { label: "CSS" },
   { label: "Nextjs " },
   { label: "ReactJS " },
   { label: "VueJS " },
   { label: "Angular " },
   { label: "Angular JS " },
   { label: "PHP language" },
   { label: "R language" },
   { label: "Objective C language" },
   { label: "Cobol language" },
   { label: "Perl language" },
   { label: "Pascal language" },
   { label: "LISP language" },
   { label: "Fortran language" },
   { label: "Swift language" },
   { label: "Ruby language" },
   { label: "Algol language" },
   { label: "Scala language" },
   { label: "Rust language" },
   { label: "TypeScript language" },
   { label: "Dart language" },
   { label: "Matlab language" },
   { label: "Ada language" },
   { label: ".NET language" },
   { label: "Bash language" },
];

function delaySleep(delay = 0) {
   return new Promise((resolve) => {
      setTimeout(resolve, delay);
   });
}

export default function App() {
   const [open, setOpen] = useState(false);
   const [dataOptions, setDataOptions] = useState([]);
   const load = open && dataOptions.length === 0;

   useEffect(() => {
      let active = true;

      if (!load) {
         return undefined;
      }

      (async () => {
         await delaySleep(1e3); // For demo purposes.

         if (active) {
            setDataOptions([...data]);
         }
      })();

      return () => {
         active = false;
      };
   }, [load]);

   useEffect(() => {
      if (!open) {
         setDataOptions([]);
      }
   }, [open]);

   return (
      <div
         style={{
            display: "flex",
            marginTop: 30,
            flexDirection: "column",
            alignItems: "center",
            justifyContent: "center",
         }}>
         <Autocomplete
            sx={{ width: 400 }}
            open={open}
            onOpen={() => {
               setOpen(true);
            }}
            onClose={() => {
               setOpen(false);
            }}
            filterOptions={(dataOptions, { inputValue }) => {
               const inputValueLowerCase = inputValue.toLowerCase();
               return dataOptions.filter((option) =>
                  option.label.toLowerCase().includes(inputValueLowerCase)
               );
            }}
            getOptionLabel={(option) => option.label}
            isOptionEqualToValue={(option, value) => option.label === value.label}
            loading={load}
            options={dataOptions}
            renderInput={(params) => ( //render the Textfield with the data options
               <TextField
                  {...params}
                  label="Search your favourite programming language"
                  InputProps={{
                     ...params.InputProps,
                     endAdornment: (
                        <div>
                           {load ? (
                              <CircularProgress color="primary" size={30} />
                           ) : null}
                           {params.InputProps.endAdornment}
                        </div>
                     ),
                  }}
               />
            )}
         />
      </div>
   );
}

Output

Updated on: 30-Oct-2023

354 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements