Bootstrap

useMemo和useCallback的用法和区别

介绍
useMemo和useCallback是React中优化是常用的两个Hooks。它们有一些共同点,比如都需要传递一个依赖数组,只有当依赖项发生改变时才会更新数据。但是它们的使用场景一般不同,下面将具体分析两个Hooks的异同。
useMemo
useMemo的定义如下:
useMemo(factory, Dependencies)
其中factory是工厂函数, Dependencies是其所有依赖值的数组。
在组件初始化的时候会调用factory函数,并将返回值存储起来。当组件需要重新渲染时,react会首先依次判断每个依赖项的值是否发生了变化。如果没有变化,则直接使用之前的存储的返回值,而不会再次调用factory,如果依赖项发生变化,则会使用更新后的依赖项调用factory函数,返回最新的值。
这个Hooks的作用是能够避免一些消耗资源的运算,从而提高性能。下面是一个例子:
import React from ‘react’

funtion MyComponent({n}) {
// 返回斐波那契数列的第n项
function Fibonacci(n){
// …
}

return 
    <div>
        斐波那契数列的第{n}项是:{Fibonacci(n)}
    </div>

}

复制代码
在上面这个组件中,每次组件重新渲染都会重新调用Fibonacci函数,即使n的值没有发生变化。这样如果n的值比较大的时候就会非常耗费性能,可以改为下面的写法。
import React, { useMemo } from ‘react’

funtion MyComponent({n}) {
// 返回斐波那契数列的第n项
function Fibonacci(n){
// …
}
const fibo = useMemo(() => Fibonacci(n), [n])
return


斐波那契数列的第{n}项是:{fibo}

}

复制代码
使用useMemo包裹后如果n的值没有发生变化,就不会重新调用Fibonacci函数,节约了性能。如果n的值足够大,节约的性能是非常可观的。
useCallback
useCallback的定义与useMemo类似:
useCallback(callback, Dependencies)
callback是一个函数对象,Dependencies是依赖项数组。
如果依赖项数组中的数据没有发生变化,则会返回上一次的callback函数。
默认情况下,每当一个组件中重新渲染,其中的函数都会重新声明。这样就会导致一个问题:如果该组件将这个函数传递给子组件,那么子组件也会重新渲染,但有时这样的渲染是不必要的。比如下面的例子:
import React from ‘react’

funtion Parent(num) {
funtion handleClick(){
// 处理点击事件
console.log(num)
}
return


{// 这里的点击回调是一个匿名的箭头函数,每次组件更新时都会重新声明}
{// 因此子组件每次接收到的回调函数都会发生变化,就会触发子组件的重新渲染}
<Child onClick={()=>handleClick()}

}

复制代码
每次父组件更新时,子组件也会重新渲染,无论子组件的内容是否发生了变化。如果需要优化可以改为下面的写法:
import React, { useCallback } from ‘react’

funtion Parent(num) {
const handleClick = useCallback(() => {
// 处理点击事件
console.log(num)
}, [num]) // 只有当num发生改变时子组件接收到的回调函数才会发生变化
return


<Child onClick={()=>handleClick()}

}

复制代码
这样如果num的值没有发生变化,子组件接收到的回调函数就不会改变,也就不会因此触发渲染。
其他
其实useCallback就是useMemo的第一个参数的返回值是函数时的特殊情况,官方文档中也说明了使用useMemo来缓存一个函数与直接使用useCallback的效果是完全相同的,只是写法上会比较复杂,比如我们可以利用useMemo实现一个我们自己的useMyCallback:
fuction useMyCallback(callback, deps){
useMemo(() => () => callback, deps)
}

复制代码
这样的效果和useCallback是完全相同的。
总结
useMemo和useCallback都是用来优化性能的Hooks。本质上useCallback是对useMemo做了一层包装,作用都是通过判断依赖项是否改变来决定是否更新值,只不过useCallback的值是一个函数对象。
useMemo一般通过减少不必要的复杂计算来优化性能,useCallback一般用于给子组件传递回调函数时,减少子组件的渲染次数,从而优化性能。

;