Debounce search input
Fetch on a debounced value, not on every keystroke.
From frontend-patterns, “Custom Hooks Patterns: Debounce Hook”.
Same typing, fewer requests
Type, or press the button to type at human pace.
Fetch per keystrokeBad
0 requests
No timer
No search yet
useEffect(() => { performSearch(query) }, [query])useDebounce(q, 500)Good
0 requests
idle
No search yet
if (debouncedQuery) performSearch(debouncedQuery)export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => {
const handler = setTimeout(() => setDebouncedValue(value), delay)
return () => clearTimeout(handler) // every new keystroke cancels the last timer
}, [value, delay])
return debouncedValue
}
const [searchQuery, setSearchQuery] = useState('')
const debouncedQuery = useDebounce(searchQuery, 500)
useEffect(() => {
if (debouncedQuery) performSearch(debouncedQuery)
}, [debouncedQuery])
Pick the delay
Set a delay, then replay. Each run is logged below.
useDebounce(query, 500)Trade-off
0 requests
– results after last key
idle
| Delay | Requests | Results after last key |
|---|---|---|
| Replay to log a run. | ||
const debouncedQuery = useDebounce(searchQuery, 500)
Late answers win
Press Type a few times; stale answers can win.
Fetch per keystrokeBad
No requests yet
No search yet
useEffect(() => { performSearch(query) }, [query])useDebounce(q, 500)Good
No requests yet
No search yet
useDebounce(query, 500)useEffect(() => {
if (!debouncedQuery) return
let ignore = false
performSearch(debouncedQuery).then((r) => { if (!ignore) setResults(r) })
return () => { ignore = true } // a pause longer than the delay can still race
}, [debouncedQuery])
Why it matters, pros and cons
Why it matters
Input changes faster than a server answers. Fetch only what holds still.
Pros
- One request per burst, not per key.
- Fewer in-flight requests to race.
- Works for any fast-changing value.
Cons
- Every result arrives at least
delaylate. - In-memory filtering needs no debounce at all.
- No ordering guarantee; still ignore stale responses.