深入 React hooks 一点点 – React 学习笔记

本文涉及内容均为个人见解,如有不正确的地方,欢迎各位大佬指正!

useCallback 和 useMemo

之前我一直以为,useCallbackuseMemo 会缓存它所使用的变量,所以如果你不把相关的变量写进依赖项里面的话就会导致这两个 hooks 拿到的不是变量最新的值。

这样的想法,对也不对。对的部分是,useCallback 和 useMemo 肯定会缓存你的数据,否则在函数组件的书写模式下,组件 props 或 state 的每一次改变都会导致组件重新渲染,另一方面,错误的依赖项也确实可能会导致你的函数拿不到所依赖的变量的最新值。而不对的地方在于事实上,useCallback 缓存的是你第一个参数传入的函数,而 useMemo 缓存的是第一个参数返回的值

先看 useCallback

useCallback 的运行逻辑类似于下面这种情况:

let state = 'matt';

const generate = (state) => {
    return () => {
        const stateCache = state;
        console.log(stateCache);
    }
}

const cachedCallback = generate(state);
复制代码

useCallback 会不仅缓存你在第一个参数函数(callback)中使用的变量,还会缓存你第一个参数传入的函数本身,以此来保证这个函数的内存地址不变。

image.png

如果想要拿到最新的值,则需要重新生成函数(callback):

image.png

而 useCallback 的第二个参数(依赖)则是决定是否需要重新生成函数(callback)的东西,只要依赖数组中有一个值与之前不全等的话,函数(callback)就会重新生成,这个时候,即便是依赖项中没有声明的变量,也会拿到最新的值(认为 useCallback 缓存了使用的变量的这个想法,会让人错误的以为只要第二个参数的依赖项没有写某个变量,第一个参数函数中的对应变量值就不会变化)!而只要依赖数组中的值与之前全等,即便函数中涉及到的变量确实已经变化,函数也不会重新生成,也就是表象上我们看到的拿不到变量的最新值。下面这个例子能够更好的理解上述情况:

export default function App() {
  const [stateA, setStateA] = useState(5);
  const [stateB, setStateB] = useState(7);

  const testFunc = useCallback(() => {
    console.log("test func 运行: ", stateA, stateB);
  }, [stateB]);
  
  useEffect(() => {
    console.log("test func 改变了");
  }, [testFunc]);

  return (
    <div className="App">
      <h1
        onClick={() => {
          console.log("改变 stateA 的值为 6");
          setStateA(6);
        }}
      >
        点击这里会改变 stateA 的值
      </h1>
      <h1
        onClick={() => {
          console.log("改变 stateB 的值为 10");
          setStateB(10);
        }}
      >
        点击这里会改变 stateB 的值
      </h1>
      <h2 onClick={testFunc}>执行 useCallback 缓存的方法</h2>
    </div>
  );
}
复制代码

image.png

上面的例子中,我们定义了两个 state, 并且分别给了它们初始值,然后使用 useCallback 定义了一个打印它们值到 console 的函数,但是依赖里面我们只声明了 stateB,然后我们通过 jsx onClick 分别改变 stateA 和 stateB 的值去观察 useCallback 的变化。

结果是,我们改变了 stateA 的值,但是由于 stateA 没有在 useCallback 的依赖声明中,testFunc 打印的值中,testFunc 并没有改变,打印出来的 stateA 也是改变前的值,而当我们点击改变了 stateB 的值之后,我们可以看到 useEffect 检测到了 testFunc 的变化,再次执行 testFunc 之后,发现不仅 stateB 变化了,stateA 也打印出来最新的值。

那 useMemo 呢?

有了上面的概念之后,再看 useMemo 就会比较好理解。

useMemo 和 useCallback 不一样的地方在于,虽然 useMemo 第一个参数也是接受一个函数,但是这个函数不能接受任何参数,且该函数最后必须返回一个值。相当于 useMemo 内部自动将第一个参数传入的函数执行了,所以可以把 useMemo 的看作是一个计算,依赖数组中的值跟之前的值不全等的情况下,就会触发 useMemo 第一个参数的重新执行,返回新的值。反之则不会重新执行。

总结

useCallback 和 useMemo 都有缓存逻辑,但 useCallback 缓存函数,useMemo 在 useCallback 的基础上会将函数自动执行,缓存其值。依赖项中任何一个值改变都会使得函数重新生成/执行!

useCallback 和 useMemo 作为 React hooks 中较为常用的 hooks,自带缓存的机制让其成为了使用 react hooks 时常用的优化手段,但错误的使用则会导致程序出现怪异的问题,所以了解透彻这两个 hooks 的原理是开始在实践中使用它的必要流程!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享