← Back to all snippets
JAVASCRIPT

Throttling Expensive Operations with `useThrottle` Hook

Discover how to create a `useThrottle` React hook to control the execution rate of functions, preventing performance issues from rapid event firing like scrolling or resizing.

import { useRef, useCallback, useEffect } from 'react';

function useThrottle(func, delay) {
  const timeoutRef = useRef(null);
  const lastArgs = useRef(null);
  const lastThis = useRef(null);

  const throttledFunc = useCallback((...args) => {
    lastArgs.current = args;
    lastThis.current = this; // Capture 'this' context

    if (!timeoutRef.current) {
      timeoutRef.current = setTimeout(() => {
        func.apply(lastThis.current, lastArgs.current);
        timeoutRef.current = null; // Reset timeout
        lastArgs.current = null;
        lastThis.current = null;
      }, delay);
    }
  }, [func, delay]);

  // Clear timeout on unmount
  useEffect(() => {
    return () => {
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
      }
    };
  }, []); // Empty dependency array means this runs once on mount and unmount

  return throttledFunc;
}

// Example Usage:
/*
function ThrottledScrollLogger() {
  const handleScroll = useCallback(() => {
    console.log('Scroll event!', new Date().toLocaleTimeString());
  }, []);

  const throttledHandleScroll = useThrottle(handleScroll, 1000); // Throttle to 1 second

  useEffect(() => {
    window.addEventListener('scroll', throttledHandleScroll);
    return () => window.removeEventListener('scroll', throttledHandleScroll);
  }, [throttledHandleScroll]);

  return (
    <div style={{ height: '2000px', background: 'lightblue', padding: '20px' }}>
      Scroll down to see throttled console logs.
    </div>
  );
}
*/
How it works: The `useThrottle` hook creates a throttled version of a given function `func`. It ensures that `func` is executed at most once within a specified `delay` period. It uses `useRef` to store the timeout ID and the arguments/context of the last call. The `useCallback` wrapper ensures the throttled function maintains referential stability. When `throttledFunc` is called, it sets a timer only if one isn't already active. If a timer is active, it just updates the `lastArgs` and `lastThis`, deferring the execution until the current timer expires. A `useEffect` cleanup ensures any pending timeout is cleared when the component unmounts.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs