All questions

useThrottledCallback

Premium

useThrottledCallback

Throttling is "at most once per interval, no matter how often you're called." Where debounce waits for silence, throttle fires on a steady cadence during a burst — perfect for scroll/resize/mousemove handlers and rate-limited actions. useThrottledCallback returns a throttled version of your function that runs on the leading edge (immediately), swallows calls within the window, and optionally fires a trailing call at the window's end with the latest arguments.

Implement useThrottledCallback(callback, delay, { leading = true, trailing = true }). Return the throttled function with .cancel() and .pending(). It must always call the current callback with the latest args, keep a stable identity, and clean up on unmount.

Signature

function useThrottledCallback(callback, delay, { leading, trailing }) {
  // returns throttled(...args) & { cancel, pending }
}

Examples

const onScroll = useThrottledCallback(() => setY(window.scrollY), 100);
window.addEventListener('scroll', onScroll); // runs ~10x/sec, not per event
// Fire only at the end of each burst, not the start.
const track = useThrottledCallback(sendAnalytics, 1000, { leading: false });

Notes

  • Leading edge — the first call in a window invokes immediately (when leading), then a delay-long window opens.
  • Within the window — calls are swallowed, but the latest args are remembered for the trailing call.
  • Trailing edge — at the window's end, fire once with the latest args (when trailing and calls occurred during the window, or a leading call was skipped).
  • Latest callback & stable identity — keep callback in a ref (refreshed each render), memoize the returned function; cancel drops a pending trailing call, pending reports one; clean up on unmount.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium