Home  >  Q&A  >  body text

How to use anti-shake hooks in React

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 hooks. However, there are some issues with using useDebounce in a resize event. useDebounce hook must be run on top of the component because it uses useEffect internally. However, the resize function is set up to run on useEffect as shown below.

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

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

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

How to use the above code to take advantage of the existing useDebounce?

P粉497463473P粉497463473287 days ago405

reply all(2)I'll reply

  • P粉098417223

    P粉0984172232024-01-29 11:40:03

    If you use the debounced function directly in the React component, it will not work because a new function will be created for 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 ensures that it is the same function as the last one provided, and useLayoutEffect ensures that on every render, the reference to the function is updated.

    For more information on this, see "Latest Reference" Pattern Reaction

    reply
    0
  • P粉894008490

    P粉8940084902024-01-29 09:59:42

    I think that instead of implementing debounce through useEffect, it is better to implement the debounce logic as a function.

    useEffect Executed when the state referenced by deps changes. In other words, since it is a logic that is easily missed if you only follow the execution process, it is difficult to figure out which process this useEffect is derived from during later maintenance, and it is also difficult to debug.

    Example

    Customized 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());
    
    
    

    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 :)

    reply
    0
  • Cancelreply