Как использовать газ или дебат с React Hook?

Я пытаюсь использовать метод throttle из lodash в функциональном компоненте, например:

const App = () => {
  const [value, setValue] = useState(0)
  useEffect(throttle(() => console.log(value), 1000), [value])
  return (
    <button onClick={() => setValue(value + 1)}>{value}</button>
  )
}

Поскольку метод внутри useEffect повторно useEffect при каждом рендеринге, эффект регулирования не работает.

У кого-нибудь есть простое решение?

Ответ 1

Вы можете (и, вероятно, нуждаетесь) useRef, чтобы сохранять значения между рендерами. Так же, как это предлагается для таймеров

Нечто подобное

const App = () => {
  const [value, setValue] = useState(0)
  const throttled = useRef(throttle((newValue) => console.log(newValue), 1000))

  useEffect(() => throttled.current(value), [value])

  return (
    <button onClick={() => setValue(value + 1)}>{value}</button>
  )
}

Что касается useCallback:

Это также может работать как

const throttled = useCallback(throttle(newValue => console.log(newValue), 1000), []);

Но если мы попытаемся воссоздать обратный вызов после изменения value:

const throttled = useCallback(throttle(() => console.log(value), 1000), [value]);

мы можем обнаружить, что это не задерживает выполнение: после изменения value обратный вызов немедленно воссоздается и выполняется.

Поэтому я вижу useCallback в случае отложенного запуска не дает значительного преимущества. Это до вас.

[UPD] изначально это было

  const throttled = useRef(throttle(() => console.log(value), 1000))

  useEffect(throttled.current, [value])

но таким образом throttled.current связывается с начальным value (из 0) закрытием. Так что он никогда не менялся даже при следующих рендерах.

Поэтому будьте осторожны при вставке функций в useRef из-за функции закрытия.

Ответ 2

Я написал два простых хука (use-throttled-effect и use-debounce-effect) для этого варианта использования, возможно, он будет полезен для кого-то, кто ищет простое решение.

import React, { useState } from 'react';
import useThrottledEffect  from 'use-throttled-effect';

export default function Input() {
  const [count, setCount] = useState(0);

  useEffect(()=>{
    const interval = setInterval(() => setCount(count=>count+1) ,100);
    return ()=>clearInterval(interval);
  },[])

  useThrottledEffect(()=>{
    console.log(count);     
  }, 1000 ,[count]);

  return (
    {count}
  );
}

Ответ 3

Если вы используете его в обработчике, я вполне уверен, что это способ сделать это.

function useThrottleScroll() {
  const savedHandler = useRef();

  function handleEvent() {}

  useEffect(() => {
    savedHandleEvent.current = handleEvent;
  }, []);

  const throttleOnScroll = useRef(throttle((event) => savedHandleEvent.current(event), 100)).current;

  function handleEventPersistence(event) {
    return throttleOnScroll(event);
  }

  return {
    onScroll: handleEventPersistence,
  };
}