react学习笔记(15) - hooks(3) - memo、useCallBack()和useMemo()
useCallBack()
- 我们都知道,在函数式组件中使用
useState中的修改方法会将整个组件重新渲染一遍,但是如果组件中存在其他内部函数的话,那么该函数就会无可避免的重新再执行一遍,也就是说,函数内部的临时变量也会无可避免的重新赋值, 但是我们反观useState中的状态, 我们在useState中定义的状态是能够被React给记住的(状态变化页面改变),那么我们能不能让React也记住我们的组件内部的函数(设置一个依赖来限制函数的重新执行,依赖改变,函数重新创建)呢?useCallBack()就能实现这样的作用!
函数式组件中,使用
useCallback对函数进行缓存(被外层函数包裹,相当于闭包),组件再次更新时(函数重新执行)会根据依赖是否变化决定选用缓存函数【之前生成的函数】还是新函数【新生成的上下文】。
一般会在嵌套组件中,与函数式组件的memo和类组件的PureComponent一起使用【会对传入props参数逐个进行浅比较决定是否需要更新】,来提高页面性能。
参数解析:
useCallback接受两个参数,第一个参数是:函数(我们在组件内部填写的函数),是箭头函数还是普通函数都没听有影响; 第二个参数是监视依赖,它决定组件更新是否生成新函数,函数内部使用到的依赖尽可能在第二个参数写全,否则会有意想不到的问题
| 何时生成新函数 | 第二个参数 |
|---|---|
| 组件首次执行及更新都会生成新函数 | 空 |
| 组件首次执行生成,之后不变 | [] |
| 组件首次执行、依赖变化时生成新函数 | [state, ref.current] 这两类 |
案例展示:
- 这个案例展示三种函数在函数式组件内部调用的过程
- 监视
age属性: 使用useCallback监视age属性,结果就是每一次age属性变化,该函数都会重新执行,获取最新的age值 - 使用
usaCallback不监视任何属性但输出name状态,结果就是因为useCallback内部没有设置监视依赖,因此输出的name永远是旧的值(初始值一直不吃不变) - **不使用
useCallback,结果就是这个函数在组件内部就是一个普通函数,组件的每一次更新都会重新调用这个函数,因此这个函数里面的值永远保持变化(内部参数每一次调用都会初始化)
- 监视
// useCallBack能够将我们的组件内部的函数记忆下来,使用依赖去限制函数重新生成和执行 |
结果展示:

useMemo
我们可以简单的认为
useMemo就是Vue中的计算属性,在React中,useMemo可以完全替代useCallback,也就是说如果你想通过使用useMemo返回一个记忆函数也是完全可以的。使用方法与上面的基本一致,只不过不同的是
useMemo的第一个参数是箭头函数且需要有返回值,返回值计位我们想要缓存的函数体*唯一的区别是:
useCallback*不会执行第一个参数函数,而是将它返回给你,useMemo会执行第一个函数并且将函数执行结果返回给你。所以
useCallback常用记忆事件函数,生成记忆后的事件函数并传递给子组件使用。而useMemo更适合经过函数计算得到一个确定的值,比如记忆组件。
案例展示:
// useMemo能够将我们的组件内部的函数记忆下来,使用依赖去限制函数重新生成和执行 |
结果展示:

memo
- 官方文档,可以认为是类组件中的
pureComonents memo用来优化函数组件的重复渲染行为,针对的是一个组件,而useMemo返回一个memoized(被记忆缓存)的值,本质都是用同样的算法来判定依赖是否发生改变,继而决定是否触发memo或者useMemo中的逻辑,利用memo就可以避免不必要的重复计算,减少资源浪费; 而useCallback则是返回一个被记忆缓存的函数;useMemo和useCallback都接收两个参数,第一个参数为fn,第二个参数为[],数组中是变化依赖的参数,memo则可以直接作用于组件
案例展示:
- 设置父子组件(父组件包裹子组件),每一次点击父组件,
num对应+1,但是子组件不会重新渲染
import React,{useState,memo} from 'react' |
结果展示:

memo结合useCallback和useMemo来优化子组件的更新操作
- 我们通常使用
memo来解决父子间通信(父=>子)传值导致子组件频繁重新渲染的问题,以及使用memo结合useMemo或者useCallback来解决子传父之间通信导致子组件频繁渲染的问题,父传子的优化直接使用memo即可,代码层面可观察上面memo的代码展示,下面展示子传父的优化方案:
分以下两种情况:
- 父子通信间(
子=>父)的嵌套,没有使用useCallback或useMemo,外层组件每次更新相当于重新生成新函数(ageChanged),传给子组件,子组件通过memo包裹对传入props参数进行浅比较,发现onClick触发的函数*有变化*,进而子组件更新(即每一次点击按钮给父组件修改数据,子组件都重新渲染)。这种方法不推荐使用
import React,{useState,memo} from 'react' |
- 结果展示:

上图可以看出,每一次点击按钮(子组件向父组件传值,父组件重新渲染),子组件都会重新渲染,频繁渲染的问题没有得到优化!
- 父子通信间(
子=>父)的嵌套使用useCallback或useMemo,且第二参数为[],也就是说该函数不监视任何依赖,即外层组件每次更新时(暂时不考虑依赖)都会复用上次函数。子组件接收到传入props的ageChanged没有变化,所以子组件不会发生更新。这种方法推荐使用
// 07-memo结合useCallback和useMemo来使用,优化子组件的频繁更新 |
- 结果展示:

上图可以看出,每一次点击按钮(子组件向父组件传值,父组件重新渲染),子组件都不会重新渲染,频繁渲染的问题得到优化!
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0许可协议。转载请注明来自 肥林の仓库




