DEV Community

Lydia_Yuan
Lydia_Yuan

Posted on

useLockFn 中 useCallback 的用途

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;
Enter fullscreen mode Exit fullscreen mode

它的作用是拦截重复的异步请求( demo: https://ahooks.js.org/hooks/advanced/use-lock-fn
我删掉了其中的 useCallback 发现还是能正常拦截的( demo: https://codepen.io/yuanruqian/pen/eYWgVpM )
所以我很疑惑这里的 useCallback 的作用是什么
于是我提了一个 issue 问了下 ahooks 的人
他们的回复是:是考虑到 performance 的问题才加的
根据回复 我写了两个 demo 比较加和不加的情况

同时根据他们的回复,我也发现了这里的一个坑:只有满足特定条件的情况下才能阻止组件重新渲染

  • 比如忘记给传入 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 更慢一些也说不定(当然简单组件慢也慢不了多少,这里只是质疑一下有些时候一些「性能优化」不一定是必要的)

推荐阅读

Discussion (0)