useMemo和useCallback的用法和区别

lxf2023-04-13 09:38:01

介绍

useMemouseCallback是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 
        <div>
            斐波那契数列的第{n}项是:{fibo}
        </div>

}

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

每次父组件更新时,子组件也会重新渲染,无论子组件的内容是否发生了变化。如果需要优化可以改为下面的写法:

import React, { useCallback } from 'react'

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

这样如果num的值没有发生变化,子组件接收到的回调函数就不会改变,也就不会因此触发渲染。

其他

其实useCallback就是useMemo的第一个参数的返回值是函数时的特殊情况,官方文档中也说明了使用useMemo来缓存一个函数与直接使用useCallback的效果是完全相同的,只是写法上会比较复杂,比如我们可以利用useMemo实现一个我们自己的useMyCallback:

fuction useMyCallback(callback, deps){
    useMemo(() => () => callback, deps)
}

这样的效果和useCallback是完全相同的。

总结

useMemouseCallback都是用来优化性能的Hooks。本质上useCallback是对useMemo做了一层包装,作用都是通过判断依赖项是否改变来决定是否更新值,只不过useCallback的值是一个函数对象。

useMemo一般通过减少不必要的复杂计算来优化性能,useCallback一般用于给子组件传递回调函数时,减少子组件的渲染次数,从而优化性能。