useThrottle
Cap how often a value updates
From the Custom Hooks collection. Returns a copy of the value that changes
at most once every delay ms, even if the source changes constantly ā the natural
pair of use-debounce.
import { useEffect, useRef, useState } from "react";
const useThrottle = <T>(value: T, delay = 500): T => {
const [throttled, setThrottled] = useState<T>(value);
const lastRun = useRef(Date.now());
useEffect(() => {
const remaining = delay - (Date.now() - lastRun.current);
if (remaining <= 0) {
lastRun.current = Date.now();
setThrottled(value); // interval already elapsed: update now.
return;
}
const id = setTimeout(() => {
lastRun.current = Date.now();
setThrottled(value); // guarantees the latest value (trailing edge).
}, remaining);
return () => clearTimeout(id);
}, [value, delay]);
return throttled;
};
export default useThrottle;
Debounce vs throttle: use-debounce waits for the source to stop for delay ms
(good for search-as-you-type); throttle releases at a steady cadence during continuous
movement (good for scroll, resize, mousemove).
Example ā scroll position updated at most every 200 ms:
const [pos, setPos] = useState(0);
useListener("scroll", () => setPos(window.scrollY), 0);
const throttledPos = useThrottle(pos, 200);
Related: Custom Hooks Ā· use-debounce Ā· use-listener.