前言
最近 codeReview 组内同学代码,发现大量的使用了 useCallback
和 useMemo
这两个 hooks,询问观点道,可以性能优化,殊不知它们使用的场景及其注意点有哪些,理解不到位反而会适得其反,下面我们拿例子分析下它们的使用场景。
useCallback
写例子前,我们回顾下它的使用方法,通过官方描述,我们知道 它返回的是缓存的函数,下面我们构造一个场景。
-
父子组件,子组件需要使用父组件的方法,一般来说,如果父组件更新了,子组件也会执行更新;但是有的场景下,更新是没有必要的,我们可以使用
useCallback
来返回函数,再传递给子组件,就达成我们想要的效果了。上面代码运行效果如下:count 只是在父组件使用的,改变它使我们子组件又重新渲染了,这是为啥呢?我来解释下:count 改变后会引起父组件的重新渲染,而每次重新渲染都会生成一个新函数,所以子组件 props 在浅比较的时候就会认为 props 改变了,引起子组件不必要的渲染。
注意: 子组件需要使用
React.memo
包裹,因为浅比较是在这里完成的。在此,Hooks FAQ 里面也表明了:创建函数的开销是不会影响性能的。
useMemo
我们也来回顾下它的使用方法,它返回的是一个 memoized
值,仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。我们可以拿 vue 里面的 computed
和它做一个对比,都是返回基于依赖重新计算的值。下面我们也来构造一些场景。
-
在组件中有两个 state:
count
和value
,它两个是没有关系的,treatCount
是根据count
的值计算得来的。上面代码运行效果如下:我们改变了
value
的值后也触发了treatCount
的重新计算,这显然是没必要的。这个时候我们使用
useMemo
优化下上面的代码:上面代码运行效果如下:是我们想要的效果了
-
父子组件,父组件传递给子组件一个引用关系的值,更新父组件其它的 state 的值,也会触发子组件的重新渲染,代码比较简单
注意: 子组件也需要使用
React.memo
包裹。上面代码运行效果如下:在我们更改了
count
值后,子组件刷新了。这个时候我们使用
useMemo
优化下上面的代码:避免了子组件不必要的更新const todoList = useMemo(() => [{ id: 1, text: "Learn React" }], []);
总结
- 如果一个组件的 props 接受的值其中含有函数,且这个函数依赖某个或多个值的时候,可以考虑使用
useCallback + React.memo
进行优化 - 当数据为引用类型,且要作为 props 传递给子组件时,可以考虑使用
useMemo + React.memo
。 - 当需要依赖某个或某些
state
做计算的时候,可以考虑使用useMemo + React.memo
。