八、useMemo和useCallback学习

一、useCallback和useMemo是什么?

useCallback和useMemo是十个hook中唯二的两个不做其他操作,只做性能优化的两个hook。

二、这两个hook是为了解决什么问题?

function组件和class组件性能优化其实要优化的点是类似的。

class组件性能优化的点:

  • 调用this.setState,就会触发组件的重新渲染,无论前后state是否相同
  • 父组件更新,子组件也会自动更新

​ 在class组件中,我们可以使用immutable去创建数据,用继承pureComponent的方式对props浅比较和用shouldComponentUpdate判断前后的 propsstate,如果没有发生变化,就返回false来阻止更新。

​ 但是在function组件中,就没有shouldComponentUpdate这个生命周期了,就无法判断前后的props是否相同,从而是不是要重新渲染。useEffect函数也没有区分是当前是mount还是update,这样一来,也就意味着,每次组件的更新都会执行其复杂的逻辑,性能的消耗将是非常大的。

​ 所以为了解决这个问题,useCallbackuseMemo就出现了。

​ useCallback、useMemo、useEffect这三者的参数是一致的。useEffect的第一个参数effect,是可以处理副作用的。而useCallback和useMemo是不可以处理副作用的。

useCallbackuseMemo 都会在组件第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行;并且这两个hooks都返回缓存的值,useMemo 返回缓存的 变量,useCallback 返回缓存的 函数。

三、React.memo()和这两个hook的区别

在class组件的时代,我们通过下面两个方法去做性能优化。

(1)pureComponent,对props进行浅比对

(2)shouldComponentUpdate,对props和state进行比较,根据返回值来处理是否要更新

对于还没有hook的function组件,我们也有方法对其进行性能优化。react提供了React.memo这样的高阶组件。它与pureComponent和相似,但是,这个高阶组件并不适用于class组件,而只为function组件服务。相比于 PureComponent ,React.memo() 可以支持指定一个参数,可以相当于 shouldComponentUpdate 的作用,因此 React.memo() 相对于 PureComponent 来说,用法更加方便。

function MyComponent () {}
 
function isEqual(prevProps, nextProps) {
  /*
  如果nextProps和prevProps的结果相同,那么就会返回true
  不相等,就返回false
  */
}
export default React.memo(MyComponent, isEqual);

看上面的代码,其实,用法很简单。在 Function Component 之外,在声明一个 isEqual 方法来判断两次 props 有什么不同,如果第二个参数不传递,则默认只会进行 props 的浅比较。最终 export 的组件,就是 React.memo() 包装之后的组件。在某些情况下,React.memo的第二个参数是必须的。

*总的来说,React.memo()方法,是让整个组件要不要re-render,记住这一点很重要,但是,有的时候,我们想要组件的某一部分要不要re-render,而不是整个组件要不要re-render.怎么办?那就用就是useMemo();*

四、useMemo() 细粒度性能优化

函数签名:

function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T; 

useMemo的第一个参数是一个工厂函数,如果传递了依赖值列表,那么在在依赖值列表发生变化时候,这个工厂函数就会执行。

用法:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useMemo() 返回的是一个 memoized 值,只有当依赖项(比如上面的 a,b 发生变化的时候,才会重新计算这个 memoized 值)

memoized 值不变的情况下,不会重新触发渲染逻辑。说到渲染逻辑,需要记住的是 useMemo() 是在 render 期间执行的,所以不能进行一些额外的副操作,比如网络请求等。如果没有提供依赖数组(上面的 [a,b])则每次都会重新计算 memoized 值,也就会 re-redner。

五、useCallback函数

函数签名:

function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;

useCallback的第一个参数是一个函数,这个函数也是useCallback去缓存的对象。每次依赖值列表发生变化的时候,useCallback会去更新这个函数,但是并不会去执行。

用法:

const fnA = useCallback(fnB, [a])

useCallback的用法其实和useMemo是一样的,但是他们唯一的区别是,useCallback是缓存了函数。

场景:在开发中,你会遇到这样的场景,需要从父组件中传递一个函数到子组件,如果我们不用useCallback包裹,那么也就意味着,只要父组件有更新,都会向子组件去传递一个新函数,虽然说每次传递的函数都一样,但是依旧是俩个不同的对象。但是如果使用了useCallback, useCallback 就会根据依赖项是否发生变化,从而决定是否返回一个新的函数,函数内部作用域也随之更新。

六、总结

  1. 在子组件不需要父组件的值和函数的情况下,只需要使用 memo 函数包裹子组件即可。
  2. 如果有函数传递给子组件,使用 useCallback
  3. 如果有值传递给子组件,使用 useMemo
  4. useEffectuseMemouseCallback 都是自带闭包的。也就是说,每一次组件的渲染,其都会捕获当前组件函数上下文中的状态(state, props),所以每一次这三种hooks的执行,反映的也都是当前的状态,你无法使用它们来捕获上一次的状态。对于需要捕获上一次状态值的情况,我们应该使用 ref 来访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码搬运工_田先森

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值