Как использовать газ или дебат с 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,
};
}