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.