Debounce получает неправильное значение аргумента при выполнении

#javascript #reactjs #typescript #next.js

#javascript #reactjs #typescript #next.js

Вопрос:

Главный вопрос заключается в том, как выполнить debounce функцию с фактическим значением переменной вместо значения, которое было фактическим 500 мс назад?

Во время выполнения debounce функция, похоже, имеет устаревшие значения аргументов из-за закрытия.

В моем коде я пытаюсь скрыть выпадающий список, только если пользователь выводит мышь из выпадающего списка минимум на 1 секунду. Поэтому, если пользователь покидает мышь из выпадающего списка, а затем сразу же вводит мышь обратно — выпадающий список все равно будет виден.

Но debounceHide функция никогда не устанавливает active dropdown в null значение, потому что она использует stillHoverOnDropdown значение, которое было фактическим 1000 мс назад (поэтому его значение равно true )

Я помещаю код ниже, но обратите внимание, что это просто для лучшего понимания. Мой код в реальном проекте намного больше, и в нем много ссылок с выпадающими списками.

Также вы увидите onHoverStart и onHoverEnd — это равно onMouseEnter и onMouseLeave . Пример:

 function debounce(fn, ms, ...args) {
  let timer;
  return () => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      timer = null;
      fn.apply(this, args);
    }, ms);
  };
}


const Navbar = () => {
  const [activeDropdown, setActiveDropdown] = useState(null);
  const [stillHoverOnDropdown, setStillHoverOnDropdown] = useState(false);

  const debounceHide = debounce(
    () => {
      if (!stillOnDropdown) {
        setActiveDropdown(null);
      }
    },
    1000
  );

  const handleHoverStart = () => {
    setStillOnDropdown(true)
  }

  const handleHoverEnd = () => {
    setStillOnDropdown(false)
    debounceHide();
  }

  return (
    <>
       <motion.a onHoverStart={() => handleDropdownHover(el.id)}>Link</motion.a>
       <motion.div onHoverStart={handleHoverStart} onHoverEnd={handleHoverEnd}>Dropdown Content</motion.div>
    </>
  )
}
  

Ответ №1:

Я нашел ответ на свой вопрос. Когда setTimeout запланировано, оно использует значение stillOnDropdown в то время, когда оно было запланировано.

useRef может решить эту проблему.

 // Use a ref to access the current count value in
// an async callback.
const countRef = useRef(count);
countRef.current = count;

const getCountTimeout = () => {
  setTimeout(() => {
    setTimeoutCount(countRef.current);
  }, 2000);
};
  

https://github.com/facebook/react/issues/14010