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许可协议。转载请注明来自 肥林の仓库