back to overview

useDebounce

5 minutes read • 16 April 23

Intro

This is a simple useDebounce React hook. It can be used to debounce a function call with trailing or leading execution.

The code

use-debounce.ts
import { useCallback, useEffect, useRef } from 'react';

type Options =
  | { leading?: false; trailing: true; }
  | { leading: true; trailing?: false; };

export function useDebounce(fn: Function, ms: number, options?: Options) {
  const locked = useRef(false);
  const timeout = useRef<ReturnType<typeof setTimeout>>();
  const callback = useRef(fn);

  useEffect(() => {
    callback.current = fn;
  }, [fn]);

  return useCallback((...params: any) => {
    /**
     * Trailing execution
     * Discard calls to the functon and keep the last one in the timeout
     * Execute the last call if the timeout is reached without a new call
     */
    if (!options?.leading) {
      timeout.current && clearTimeout(timeout.current);
      timeout.current = setTimeout(() => {
        callback.current(...params);
        timeout.current = undefined;
      }, ms);
    }

    /**
     * Leading execution
     * Execute the callback immediatly
     * Discard the following executions until the timeout is reached without a new call
     * Unlock the execution when the timeout is reached
     */
    if (options?.leading) {
      !locked.current && callback.current(...params);
      locked.current = true;
      timeout.current && clearTimeout(timeout.current);
      timeout.current = setTimeout(() => {
        locked.current = false;
      }, ms);
    }
  }, [ms]);
};