【React】 useCallback&useMemo 你真的会用吗

7,397

前言

最近 codeReview 组内同学代码,发现大量的使用了 useCallbackuseMemo 这两个 hooks,询问观点道,可以性能优化,殊不知它们使用的场景及其注意点有哪些,理解不到位反而会适得其反,下面我们拿例子分析下它们的使用场景。

useCallback

写例子前,我们回顾下它的使用方法,通过官方描述,我们知道 它返回的是缓存的函数,下面我们构造一个场景。

  1. 父子组件,子组件需要使用父组件的方法,一般来说,如果父组件更新了,子组件也会执行更新;但是有的场景下,更新是没有必要的,我们可以使用 useCallback 来返回函数,再传递给子组件,就达成我们想要的效果了。

    Untitled (1).jpeg

    Untitled (2).jpeg

    上面代码运行效果如下:count 只是在父组件使用的,改变它使我们子组件又重新渲染了,这是为啥呢?我来解释下:count 改变后会引起父组件的重新渲染,而每次重新渲染都会生成一个新函数,所以子组件 props 在浅比较的时候就会认为 props 改变了,引起子组件不必要的渲染。

    注意: 子组件需要使用 React.memo 包裹,因为浅比较是在这里完成的。

    在此,Hooks FAQ 里面也表明了:创建函数的开销是不会影响性能的。

    QQ20220404-105614.gif

useMemo

我们也来回顾下它的使用方法,它返回的是一个 memoized 值,仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。我们可以拿 vue 里面的 computed和它做一个对比,都是返回基于依赖重新计算的值。下面我们也来构造一些场景。

  1. 在组件中有两个 state:countvalue,它两个是没有关系的,treatCount 是根据 count 的值计算得来的。

    Untitled (1).jpeg

    上面代码运行效果如下:我们改变了 value 的值后也触发了 treatCount 的重新计算,这显然是没必要的。

    QQ20220404-164343.gif

    这个时候我们使用 useMemo 优化下上面的代码:

    Untitled.jpeg

    上面代码运行效果如下:是我们想要的效果了

    QQ20220404-164821.gif

  2. 父子组件,父组件传递给子组件一个引用关系的值,更新父组件其它的 state 的值,也会触发子组件的重新渲染,代码比较简单

    Untitled (2).jpeg

    Untitled (4).jpeg

    注意: 子组件也需要使用 React.memo 包裹。

    上面代码运行效果如下:在我们更改了 count 值后,子组件刷新了。

    QQ20220404-175626.gif

    这个时候我们使用 useMemo 优化下上面的代码:避免了子组件不必要的更新

    const todoList = useMemo(() => [{ id: 1, text: "Learn React" }], []);
    

总结

  1. 如果一个组件的 props 接受的值其中含有函数,且这个函数依赖某个或多个值的时候,可以考虑使用 useCallback + React.memo 进行优化
  2. 当数据为引用类型,且要作为 props 传递给子组件时,可以考虑使用 useMemo + React.memo
  3. 当需要依赖某个或某些 state 做计算的时候,可以考虑使用 useMemo + React.memo