Prevent race conditions with request identity
When a search box triggers requests as the user types, an older response can arrive after a newer one and overwrite the correct results. Use a monotonically increasing request ID to ensure only the latest request updates state.
function useLatestSearch(query: string) {
const requestId = useRef(0);
const [results, setResults] = useState<string[]>([]);
useEffect(() => {
const id = ++requestId.current;
async function search() {
const response = await fetch(
`/api/search?q=${encodeURIComponent(query)}`,
);
if (!response.ok) throw new Error("Search failed");
const data = await response.json();
if (id === requestId.current) {
setResults(data);
}
}
search().catch(console.error);
return () => {
requestId.current++;
};
}, [query]);
return results;
}
Why it matters: Correctness no longer depends on network response order.
Production pitfalls:
- Handle errors in UI state rather than only logging them.
- Prefer
AbortControllerwhen supported by the underlying operation to cancel unnecessary network work. - In React Strict Mode, effects can run an extra setup/cleanup cycle in development. Effects must be safe to repeat.