skip to Main Content
import { useEffect, useState } from 'react';

export default function useDebounce(text: string, delay: number) {
  const [value, setValue] = useState('');

  useEffect(() => {
    const timerId = setTimeout(() => {
      setValue(text);
    }, delay);
    return () => {
      clearTimeout(timerId);
    };
  }, [text, delay]);
  return value;
}


I used to make and use useDebounce hook.
However, using useDebounce in resize event has some problems.
The useDebounce hook must run at the top of the component because it uses useEffect inside.
However, the resize function is set to run on useEffect as shown below.

Also, the above code takes value as a factor, but I think we need to receive it as callback to use the below code.

  useEffect(() => {
    const handler = () => {
      if (liRef.current) setWidth(liRef.current.clientWidth);
    };

    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, []);

How do I use the above code to utilize the existing useDebounce?

2

Answers


  1. I think rather than implementing debounce through useEffect, it would be better to implement the debounce logic as a function.

    UseEffect is executed when the states referenced by deps change. In other words, since it is a logic that can be missed if you only follow the execution flow, it is difficult to figure out which flow this useEffect was derived from when performing maintenance later, and it can be difficult to debug.

    Example

    Custom Debounce

    function debounce(func, timeout = 300) {
      let timer;
      return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => {
          func.apply(this, args);
        }, timeout);
      };
    }
    
    function saveInput() {
      console.log('Saving data');
    }
    const processChange = debounce(() => saveInput());
    
    <input type="text" onkeyup="processChange()" />
    

    And if you use lodash, you can just import to use it.

    Lodash Debounce

    import { debounce } from 'lodash';
    
    const debounceOnChange = debounce(() => {
      console.log("This is a debounce function");
    }, 500);
    

    Hope this helps 🙂

    Login or Signup to reply.
  2. If you directly use debounced function inside a react component, it will not work, since a new function will be created on each render, Instead, you can use this useDebounce hook:

    function useDebounce(callback, delay) {
      const callbackRef = React.useRef(callback)
      React.useLayoutEffect(() => {
        callbackRef.current = callback
      })
      return React.useMemo(
        () => debounce((...args) => callbackRef.current(...args), delay),
        [delay],
      )
    }
    

    useRef makes sure that it is the same function provided last time, and useLayoutEffect makes sure on each render, the reference to function gets updated.

    For more info on this, see ‘The Latest Ref’ pattern in React

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search