Part 39: Debouncing & Throttling: Optimizing User Input & Search
When a user types into a live search box, firing an API request on every keystroke can overwhelm backend servers and create race conditions.
1. Debounce vs. Throttle
- Debounce: Delays execution until a specified quiet period has elapsed after the last event. (Ideal for search inputs and auto-saving).
- Throttle: Guarantees execution at most once per specified time interval. (Ideal for scroll listeners, window resizing, and mouse movement).
2. Creating a Reusable useDebounce Hook
import { useState, useEffect } from 'react';
export function useDebounce(value, delay = 300) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// Cancel timer if value changes before delay finishes
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
3. Real-World Live Search Component
import { useState, useEffect } from 'react';
import { useDebounce } from './useDebounce';
export default function SearchAutocomplete() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearch = useDebounce(searchTerm, 400);
const [results, setResults] = useState([]);
useEffect(() => {
if (!debouncedSearch.trim()) {
setResults([]);
return;
}
async function searchApi() {
const res = await fetch(`/api/search?q=${encodeURIComponent(debouncedSearch)}`);
const data = await res.json();
setResults(data);
}
searchApi();
}, [debouncedSearch]);
return (
<div>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Type to search..."
className="border p-2 rounded w-full"
/>
<ul>
{results.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>
);
}
Key Takeaways
- Debounce search inputs to prevent server overload and race conditions.
- Implement
useDebouncewithsetTimeoutand cleanup viaclearTimeout.
In Part 40, we enter Module 7 to explore Code Splitting with React.lazy and Suspense!

