Timing
Debounce, throttle, and interval/timeout scheduling, with proper cleanup on unmount.
useDebouncedCallback
Fires a callback only after the value stops changing for delay(ms). Useful for search inputs, autosave, etc.
const [text, setText] = useState('');
const [debounced, setDebounced] = useState('');
useDebouncedCallback(() => setDebounced(text), { delay: 400 }, [text]);
useDebouncedValue
The value-shaped counterpart to useDebouncedCallback — symmetric with useThrottledValue's (value, delay) => value signature, for when all you need is the debounced value itself.
const [text, setText] = useState('');
const debounced = useDebouncedValue(text, 400);
useThrottledValue
Applies the latest value only at delay(ms) intervals, even if it changes rapidly. Useful for scroll/resize handling.
const [value, setValue] = useState(0);
const throttled = useThrottledValue(value, 500);
useThrottledCallback
Throttles a callback directly (unlike useThrottledValue, which throttles a value) — the natural fit for scroll/mousemove/resize handlers. Supports leading/trailing options.
const onMouseMove = useThrottledCallback(
(x: number, y: number) => setPosition({ x, y }),
200,
);
<div onMouseMove={e => onMouseMove(e.clientX, e.clientY)} />;
useTimeout
A setTimeout that doesn't go stale — the callback is read from a ref, delay === null pauses it (0 is a valid delay), and reset/clear let you restart or cancel imperatively.
const [open, setOpen] = useState(false);
const { reset, clear } = useTimeout(() => setOpen(false), open ? 2000 : null);
// Pause the auto-dismiss while hovered, restart it on mouse leave
<div onMouseEnter={clear} onMouseLeave={reset}>
Toast
</div>;
useInterval
Dan Abramov's useInterval pattern — the callback is read from a ref so a fresh function every render doesn't reset the interval, only delay === null (pause) vs a number (running) does.
const [running, setRunning] = useState(false);
const [count, setCount] = useState(0);
useInterval(() => setCount(c => c + 1), running ? 1000 : null);
useRecursiveTimeout
Repeats a callback using recursive setTimeout instead of setInterval. Pass null as delay to stop.
const [tick, setTick] = useState(0);
useRecursiveTimeout(
() => {
setTick(t => t + 1);
},
running ? 1000 : null,
);