A search box that queries the server on every keystroke sends ten requests for a six-letter word. Debouncing returns a copy of the value that changes only once the user has paused, and the Hook fits in nine lines because setTimeout and Effect cleanup do all the work.
import { useState, useEffect } from 'react';
export function useDebounce(value, delay = 400) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const id = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(id); // a new keystroke cancels the pending update
}, [value, delay]);
return debounced;
}The component keeps the input controlled and fully responsive — const [text, setText] = useState('') — and fetches from the slow copy: const query = useDebounce(text, 300);, with query as the request Effect's only dependency. Each new character re-runs the debouncing Effect, whose cleanup clears the timer the previous character started, so the update lands 300 ms after the last change and not before.
Debouncing waits for silence; throttling lets one call through per interval and suits events that never stop, such as scroll. For both, plus a cancel and flush API, use-debounce 10.1.1 3,384 (MIT, github.com/xnimorz/use-debounce (https://github.com/xnimorz/use-debounce 3,384 )) is the standard package. But first check whether the delay is really about the network: when the cost is rendering rather than fetching, useDeferredValue (useDeferredValue) is better, because it yields to the browser instead of guessing at a number of milliseconds.