Enable Dark Mode!
how-to-build-an-api-search-with-debouncing-in-react.jpg
By: Alan Joy

How to Build an API Search with Debouncing in React

Technical odoo React

Search is a very common feature in many React applications. We will use a search field to get data from api, like searching for products, customers, articles, users, etc. In these cases, a simple search input works, but there is one problem: every time the user types a character, the application will send a new api request. These unnecessary requests can increase the server load. It can also consume network resources and make the search experience less efficient and laggy.

Debouncing is a simple technique that helps solve this problem. Instead of making api requests on every keystroke, we will wait for the user to stop typing for a short period of time and then make a single api request. In this blog, we will build a React application with this search feature.

What Is Debouncing?

Debouncing is a feature that delays a function until a specific amount of time has passed without it being called again. In the case of search input, this means waiting until the user pauses typing before sending any api request. This approach reduces unnecessary api requests and keeps the search experience very responsive.

1. Create a React Project

Create a new React project by running this command.

npm create vite@latest react-api-search -- --template react
cd react-api-search
npm install
npm run dev

2. Create a Search Input

First, we will create a search input and store the search input in a useState. Then, the result will be returned by api. Create a new component named ApiSearch.jsx.

import { useState } from "react";
export default function ApiSearch() {
 const [search, setSearch] = useState("");
 const [results, setResults] = useState([]);
 return (
   <div>
     <input
       value={search}
       onChange={(event) => setSearch(event.target.value)}
       placeholder="Search users..."
     />
     {results.map((user) => (
       <p key={user.id}>{user.name}</p>
     ))}
   </div>
 );
}

3. Fetch Data from an API

For this example, we are going to use jsonplaceholder api for the search data. We can search users by using the name query parameter in jsonplaceholder api. Update the component with this.

import { useEffect, useState } from "react";
useEffect(() => {
 const fetchUsers = async () => {
   const response = await fetch(
     `https://jsonplaceholder.typicode.com/users?name_like=${encodeURIComponent(search)}`
   );
   const data = await response.json();
   setResults(data);
 };
 fetchUsers();
}, [search]);

4. Add Debouncing with useEffect

React’s own useEffect makes it easy to implement the debouncing feature without installing any new package. We can use setTimeout to delay the request and clearTimeout to cancel the timer when the search values are changed again. Update the component and replace the useEffect with this.

useEffect(() => {
 if (!search.trim()) {
   setResults([]);
   return;
 }
 const timer = setTimeout(async () => {
   try {
     const response = await fetch(
       `https://jsonplaceholder.typicode.com/users?name_like=${encodeURIComponent(search)}`
     );
     if (!response.ok) {
       throw new Error("Failed to fetch users.");
     }
     const data = await response.json();
     setResults(data);
   } catch (error) {
     console.error(error);
     setResults([]);
   }
 }, 500);
 return () => clearTimeout(timer);
}, [search]);

5. Display the Search Results

Now, we need to display the results sent by the api below the input. Add this code to the component.

<div>
 {results.length > 0 ? (
   results.map((user) => (
     <p key={user.id}>{user.name}</p>
   ))
 ) : search.trim() ? (
   <p>No users found.</p>
 ) : null}
</div>

6. Add a Loading State

Now, we can add a loading state. When searching the api, the response can take some time to arrive. By showing a loading indication helps the user to understand that the application is processing their search. Add a loading state and update the component

const [loading, setLoading] = useState(false);
useEffect(() => {
 if (!search.trim()) {
   setResults([]);
   setLoading(false);
   return;
 }
 const timer = setTimeout(async () => {
   setLoading(true);
   try {
     const response = await fetch(
       `https://jsonplaceholder.typicode.com/users?name_like=${encodeURIComponent(search)}`
     );
     if (!response.ok) {
       throw new Error("Failed to fetch users.");
     }
     const data = await response.json();
     setResults(data);
   } catch (error) {
     console.error(error);
     setResults([]);
   } finally {
     setLoading(false);
   }
 }, 500);
 return () => clearTimeout(timer);
}, [search]);
{loading && <p>Searching...</p>}
{!loading && results.length > 0 &&
 results.map((user) => (
   <p key={user.id}>{user.name}</p>
 ))}
{!loading && search.trim() && results.length === 0 && (
 <p>No users found.</p>
)}

7. Complete code

import { useEffect, useState } from "react";
export default function ApiSearch() {
 const [search, setSearch] = useState("");
 const [results, setResults] = useState([]);
 const [loading, setLoading] = useState(false);
 useEffect(() => {
   if (!search.trim()) {
     setResults([]);
     setLoading(false);
     return;
   }
   const timer = setTimeout(async () => {
     setLoading(true);
     try {
       const response = await fetch(
         `https://jsonplaceholder.typicode.com/users?name_like=${encodeURIComponent(search)}`
       );
       if (!response.ok) {
         throw new Error("Failed to fetch users.");
       }
       const data = await response.json();
       setResults(data);
     } catch (error) {
       console.error(error);
       setResults([]);
     } finally {
       setLoading(false);
     }
   }, 500);
   return () => clearTimeout(timer);
 }, [search]);
 return (
   <div>
     <input
       value={search}
       onChange={(event) => setSearch(event.target.value)}
       placeholder="Search users..."
     />
     <div>
       {loading && <p>Searching...</p>}
       {!loading && results.length > 0 &&
         results.map((user) => (
           <p key={user.id}>{user.name}</p>
         ))}
       {!loading && search.trim() && results.length === 0 && (
         <p>No users found.</p>
       )}
     </div>
   </div>
 );
}

Debouncing is a simple and very useful technique to improve the performance of api searches and functionalities in React. With the use of useState, useEffect, setTimeout, and clearTimeout, we can implement a debouncing function without using any additional libraries or packages. We can use this same approach for handling other use cases like product searches, customer lookup forms, autocomplete inputs, and other features that are based on user inputs.

To read more about How to Validate API Responses in React Using Zod, refer to our blog, How to Validate API Responses in React Using Zod.


Frequently Asked Questions

What is debouncing in React?

Debouncing delays an action until the user stops triggering it for a specified time. It helps reduce unnecessary API requests when typing in a search input.

What is a good debounce delay for API search?

A delay of 300–500 milliseconds is a good starting point. You can adjust it depending on your application's requirements and user experience.

How do I show a loading indicator during API search?

Use React's useState to manage a loading flag. Set it to true when the API request starts and reset it to false in a finally block when the request finishes.

If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



WhatsApp