ahooks 库里面有一个 useLockFn
import { useRef, useCallback } from 'react';
function useLockFn<P extends any[] = any[], V extends any = any>(fn: (...args: P) => Promise<V>) {
const lockRef = useRef(false);
return useCallback(
async (...args: P) => {
if (lockRef.current) return;
lockRef.current = true;
try {
const ret = await fn(...args);
lockRef.current = false;
return ret;
} catch (e) {
lockRef.current = false;
throw e;
}
},
[fn],
);
}
export default useLockFn;
它的作用是拦截重复的异步请求( demo: https://ahooks.js.org/hooks/advanced/use-lock-fn )
我删掉了其中的 useCallback
发现还是能正常拦截的( demo: https://codepen.io/yuanruqian/pen/eYWgVpM )
所以我很疑惑这里的 useCallback
的作用是什么
于是我提了一个 issue 问了下 ahooks 的人
他们的回复是:是考虑到 performance 的问题才加的
根据回复 我写了两个 demo 比较加和不加的情况
- 加
useCallback
避免用到返回的 memoized 函数的子组件进行不必要的渲染: https://codepen.io/yuanruqian/pen/yLbgqEm - 不加
useCallback
,每次返回的是新生成的一个函数,子组件就算memo
了也会跟着父组件一起渲染:https://codepen.io/yuanruqian/pen/rNmjreX
同时根据他们的回复,我也发现了这里的一个坑:只有满足特定条件的情况下才能阻止组件重新渲染
- 比如忘记给传入
useLockFn
的函数加上useRef
或者useCallback
,每次父组件刷新都会生成一个新函数传给useLockFn
,加的useCallback
就没有发挥出「防止不必要的重渲染」的作用 demo:https://codepen.io/yuanruqian/pen/yLbgqEm
综上,这个 hook 能发挥「防止不必要的重渲染」的 条件如下
- 传入的
useLockFn
的函数不变(需要用useRef
或者useCallback
进行处理) - 用到这个函数的组件需要用
memo
进行缓存 (这也可以说是useCallback
的一个坑,因为它往往需要和memo
一起组合使用才能防止重新渲染) - 组件里没有其他改变 state 的 hook,如果里面有一个
useState
就前功尽弃了
如果不能做到这些条件,那么 useCallback
只是白占了空间和时间资源而已( useCallback
内部实现会记录 prevState 占内存,每次都会比较 deps 数组的变化占时间)
而且就算能做到这些条件,对于大部分的开发项目来说,真的有必要这样做吗?React 渲染 UI 的速度是很快的,除了一些非常复杂的交互图等之外,或许因为上面说的时空耗费比本来 re-rendering 更慢一些也说不定(当然简单组件慢也慢不了多少,这里只是质疑一下有些时候一些「性能优化」不一定是必要的)
推荐阅读
Top comments (0)