React文档中说:
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。
那。。。有啥区别呢?分别什么场景使用呢?在讲区别之前,先来看看useCallback和useMemo的具体使用:
示例
App.js
button.js
效果如下
可以发现,点击任意一个,另外两个组件都会重新加载。接下来 我们对Comp组件使用Memo进行包裹一下:
再试一下:
现在发现,当我们点击第一个和第三个组件的时候,第二个组件是不会重新渲染的,只有当我们点击第二个,触发num2改变的时候,第二个组件才会重新渲染。
这里是用到了React.memo()会对组件的props进行一次浅比较,如果相同则不会重新渲染。但是组件都被Memo包裹了,为什么只有组件2没有触发无效更新呢? 区别就在于我们对方法2使用了useCallback进行包裹。1和3又为啥会重新渲染呢,明明都用memo包裹了啊?因为props接收的我们定义的方法,每次方法都是重新生成的,即使方法内容一样,但是fn != fn。
把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。—————React
依赖空数组?
上述代码修改一下,把方法2的依赖项num2给删除,只给一个空数组:
效果如下:
点击其他组件的时候不会触发组件2的重新渲染,但是点击组件2的时候,只会触发一次重新渲染,后面再也不会发生变化。why?debugger 看下之后发现,setNum()每次都会执行,但是num2永远都是1,所以每次都是setNum(1+1) ,那么组件接收到的num永远都是2,所以不会重新渲染了。
所以,依赖别忘记了。
将方法全部用useCallback包裹一下?
NO!因为每次执行方法的时候都需要进行一次对比,这也是消耗性能的。而且React指出:
当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。
即需要配合SCU或者memo来搭配使用才是好东西,如果单独使用则可能效果相反了。
useMemo
我们知道useMemo会对新旧props进行比对来决定组件是否需要重新渲染。它和useCallBack的第一点区别就是useMemo返回的是一个memoized值,而useCallback返回的一个memoized函数。
useMemo不需要和其他搭配使用,useMemo其实有两个目的: 1、对于对象的一个缓存,比如:
上图中即使name或者price没有变化,每次渲染poi也是不一样的,因为{}!={}。
当我们使用useMemo包裹后,只有poiName或者poiPrice发生改变后,组件才会发生重新渲染:
2、对于复杂计算结果的缓存
把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。
总结
- useCallback返回的函数,是为了避免子组件不必要的重新渲染,优化子组件的更新
- useMemo返回的是值,可以用来优化子组件也可以用来优化当前组件,另外可以对一些复杂计算的结果进行缓存。
自己实现一个useCallback
本文首发于公众号 前端纪元 欢迎关注