问题
I'm trying to use the throttle
method from lodash in a fonctionnal component, e.g.:
const App = () => {
const [value, setValue] = useState(0)
useEffect(throttle(() => console.log(value), 1000), [value])
return (
<button onClick={() => setValue(value + 1)}>{value}</button>
)
}
Since the method inside useEffect
is redeclared at each render, the throttling effect does not work.
Does anyone have a simple solution ?
回答1:
you may(and probably need) useRef to store value between renders. Just like it's suggested for timers
Something like that
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>
)
}
As for useCallback
:
It may work too as
const throttled = useCallback(throttle(newValue => console.log(newValue), 1000), []);
But if we try to recreate callback once value
is changed:
const throttled = useCallback(throttle(() => console.log(value), 1000), [value]);
we may find it does not delay execution: once value
is changed callback is immediately re-created and executed.
So I see useCallback
in case of delayed run does not provide significant advantage. It's up to you.
[UPD] initially it was
const throttled = useRef(throttle(() => console.log(value), 1000))
useEffect(throttled.current, [value])
but that way throttled.current
has bound to initial value
(of 0) by closure. So it was never changed even on next renders.
So be careful while pushing functions into useRef
because of closure feature.
回答2:
I wrote two simple hooks (use-throttled-effect and use-debounced-effect) for this use case maybe it wil be useful for someone else looking for a simple solution.
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:
If you are using it in handler, I am fairly certain this is the way to do it.
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,
};
}
来源:https://stackoverflow.com/questions/54666401/how-to-use-throttle-or-debounce-with-react-hook