前言
React 函数式组件使用 throttle / debounce 有诸多注意事项。
否则会有回调函数只是延迟执行,执行次数并没有减少的情况发生。
正文
useCallback
throttle / debounce 的回调函数如果不用 useCallback 包裹的话,在每一次状态更新的时候,回调函数都会是一个新的对象。
这就会导致 throttle / debounce 只会推迟回调函数的执行,并不会减少回调函数的调用次数。
如果用 useCallback 包裹回调函数,就会保证回调函数都是同一个。
dependencies
注意回调函数,如果内部有嵌套调用的话,每一层都需要走查,确保 useCallback 的 dependencies 中的任意一项,在这些回调函数中,每一项都没有被重新赋值。
踩坑实例
如下述的代码,ParentComponent 中,在 debounce 回调函数中被调用的 onChange 函数的依赖添加了不应该添加的 count,而 count 在 ChildComponent 中调用 debounceOnChange 前都会被赋予新值,这就导致 debounce 的回调函数每一次返回的也是新的。
就会出现虽然 debounce 了,但是回调函数只是延迟执行了,调用次数并没有减少的情况。
解决方法是去除 ParentComponent 的 onChange 函数中多余的依赖 count
const ChildComponent = ({ updateCount, onChange }) => {
const [ownCount, setOwnCount] = useState(0);
const debounceOnchange = useCallback(() => {
debounce((otherDatas) => {
onChange?.(otherDatas)
}, 1000),
[onChange]
}, []);
useEffect(() => {
updateCount(ownCount);
const otherDatas = someAsyncCalFunctions(ownCount);
debounceOnchange(otherDatas);
}, [ownCount]);
return <div>
xxxx
<Button onClick={() => setOwnCount(v => v + 1)}>Click</Button>
</div>
}
const ParentComponent = () => {
const [count, setCount] = useState(0);
const updateCount = useCallback((val) => {
setCount(val);
}, []);
const onChange = useCallback((data) => {
// do something using `data` & `otherDeps`
// but without `count`
},
[otherDeps, count]
);
return <div>
xxx
<ChildComponent
updateCount={updateCount}
onChange={onChange}
/>
</div>
}