tech

useDebounceUpdate - Running an effect after a timeout

Execute a function after a timeout every time your data changes. Reset the timer if the data changes again during the timeout.


Sandro Maglione

Sandro Maglione

Software development

Execute a function after a timeout every time your data changes. Reset the timer if the data changes again during the timeout.

Sometimes when the user updates some state, we want to make an API request to sync the state remotely. We cannot run a function every time the user changes the data however.

What we do instead is debounce the state update, i.e. waiting a fixed amount of time after the user stops interacting before making the request. Furthermore, we want to reset the timeout if in the meantime the user changes the state again.

Well, that's what useDebounceUpdate is made for!

isFirstRender is used to avoid running execute at first render (which is probably something that you don't want here 💁🏼‍♂️).

Every time data changes timer resets and the execute function will be called again only after timeout milliseconds.

useDebounceUpdate.ts
import { useEffect, useRef } from "react";

/**
 * Run the `execute` function after `timeout` milliseconds.
 * If the value of `data` changes, reset the timer.
 */
export const useDebounceUpdate = <Data>({
  execute,
  data,
  timeout = 2000,
}: {
  data: Data;
  timeout?: number;
  execute: (value: Data) => void;
}): void => {
  const isFirstRender = useRef(true);
  const timer = useRef<ReturnType<typeof setTimeout>>();
  useEffect(() => {
    if (!isFirstRender.current) {
      if (timer.current) {
        clearTimeout(timer.current);
      }

      timer.current = setTimeout(() => {
        execute(data);
      }, timeout);
    } else {
      isFirstRender.current = false;
    }
  }, [data]);
};

👋・Interested in learning more, every week?

Timeless coding principles, practices, and tools that make a difference, regardless of your language or framework, delivered in your inbox every week.