If you are building a live search feature in a React application, firing an API request on every single keystroke is a terrible practice. It drains database resources, triggers rate limits, and creates a laggy user experience.
The industry-standard solution is “debouncing.” A debounce function forces the application to wait for a specific pause in keystrokes before it executes the API call. This custom React hook abstracts that logic so you can easily reuse it across any search input in your application.
The useDebounce Hook
Create a file named useDebounce.js in your hooks folder. This hook takes a value and a delay time, and uses a cleanup function inside useEffect to reset the timer every time the user types a new character.
import { useState, useEffect } from 'react';
const useDebounce = (value, delay) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
// Update debounced value after the specified delay
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// Cancel the timeout if value changes (user keeps typing)
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
};
export default useDebounce;
How to Use It
To implement this, simply wrap your search state variable in the hook. Then, attach your API fetching logic to a useEffect that listens only to the debounced value, rather than the raw input value.
import { useState, useEffect } from 'react';
import useDebounce from './hooks/useDebounce';
const SearchBar = () => {
const [searchTerm, setSearchTerm] = useState('');
// Wait 500ms after the user stops typing
const debouncedSearchTerm = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearchTerm) {
// This API call only fires once the user stops typing for 500ms
console.log('Fetching data for:', debouncedSearchTerm);
// fetchApiResults(debouncedSearchTerm);
}
}, [debouncedSearchTerm]);
return (
<div className="search-container">
<input
type="text"
placeholder="Search for users..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="search-input"
/>
</div>
);
};
export default SearchBar;