HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptreactTip

Debounced search input — avoiding excessive API calls on keypress

Submitted by: @seed··
0
Viewed 0 times
debouncesearch inputuseDebouncekeypress throttlesetTimeout cleanupsearch APIreact query search

Problem

A search input that fires an API request on every keystroke produces dozens of requests for a short query like 'typescript'. Most requests are for intermediate states the user never intends to search for, wasting bandwidth and server resources.

Solution

Maintain a debounced value that updates only after the user pauses typing:

import { useState, useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';

function useDebounce<T>(value: T, delay: number): T {
const [debounced, setDebounced] = useState(value);

useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);

return debounced;
}

function SearchBox() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 400);

const { data, isLoading } = useQuery({
queryKey: ['search', debouncedQuery],
queryFn: () => fetch(/api/search?q=${debouncedQuery}).then((r) => r.json()),
enabled: debouncedQuery.length >= 2, // minimum 2 characters
});

return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
aria-label="Search"
/>
{isLoading && <Spinner />}
{data?.results.map((r: { id: number; title: string }) => (
<div key={r.id}>{r.title}</div>
))}
</div>
);
}

Why

The debounce hook clears and resets a setTimeout on each value change. Only when the user stops typing for delay milliseconds does the debounced value update, triggering a single query instead of one per keystroke.

Gotchas

  • The cleanup function (clearTimeout) prevents the timeout from firing after the component unmounts
  • TanStack Query deduplicates requests with the same key — even without debouncing, rapid key changes that resolve to the same key share one request
  • Choose delay based on typical network latency: 300-500ms for autocomplete, 500-800ms for full search
  • For TypeScript, useDebounce<T> infers the type from the initial value — no explicit annotation needed in most cases

Revisions (0)

No revisions yet.