React中memo(),useMemo(),useCallback()的使用(区别,解决了什么问题)

React中memo(),useMemo(),useCallback()的使用

前言:React框架中,当组件的props或state发生变化时,会重新渲染组件,实际开发中会遇到不必要的重新渲染场景。这里的memo(),useMemo(),useCallback()都是应用在函数组件中。调理不够清楚,希望你能够一步一步看。

React.memo()

React.memo()和类组件中React.PureComponent()很相似,它可以帮助我们控制什么时候重新渲染组件,它适用于函数组件,但不适用于 class 组件。如果你的函数组件在给定相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。
React.memo 仅检查 props 变更。如果函数组件被 React.memo 包裹,且其实现中拥有 useState 或 useContext 的 Hook,当 context 发生变化时,它仍会重新渲染。
默认情况下其只会对复杂对象做浅层对比,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现。

下面看一个很简单的例子:

import React, { useState } from "react";
//子组件
const ChildCom = () => {
    console.log('render child component');
    return (
        <div>ChildComponent</div>
    )
};

//父组件
const ParaentCom = () => {
    const [count, setCount] = useState(0);
    const increment = () => setCount(count + 1);
    return (
        <div>
            <button onClick={increment}>点击加1</button>
            <div>点击次数:{count}</div>
            <ChildCom />
        </div>
    )
}
export default ParaentCom;

React中memo(),useMemo(),useCallback()的使用(区别,解决了什么问题)_第1张图片

子组件中有console语句,在函数组件中,每次重新渲染都会重新打印输出。我们点击父组件中按钮,会修改count的值,进而导致父组件重新渲染,但是我们发现即使子组件的props和state没有变化,但控制台显示子组件也重新渲染了。但是我们这里期望的是:子组件的props和state没有变化时,即使父组件渲染,子组件也不需要重新渲染。

解决方法:使用React.memo()将子组件进行包裹

import React, { memo,useState } from "react";
//子组件
const ChildCom = memo(() => {
    console.log('render child component');
    return (
        <div>ChildComponent</div>
    )
});

//父组件
const ParaentCom = () => {
    const [count, setCount] = useState(0);
    const increment = () => setCount(count + 1);
    return (
        <div>
            <button onClick={increment}>点击加1</button>
            <div>点击次数:{count}</div>
            <ChildCom />
        </div>
    )
}
export default ParaentCom;

React中memo(),useMemo(),useCallback()的使用(区别,解决了什么问题)_第2张图片
子组件使用React.memo包裹之后,我们多次点击按钮,发现子组件没有重新渲染。

React.useMemo()

上面的例子只是最简单的父组件没有向子组件传递props的情况,如果传递props时,我们怎么控制子组件不重复渲染或者只让某些props中的属性变化时子组件才重新渲染,这就需要用到 React.useMemo()。

useMemo 有两个参数:

  • 第一个参数是个函数,返回的对象指向同一个引用,不会创建新对象;
  • 第二个参数是个数组,只有数组中的变量改变时,第一个参数的函数才会返回一个新的对象。

看一个简单例子

import React, { memo,useState } from "react";
//子组件
const ChildCom = memo(({ number }) => {
    console.log('render child component', number);
    return (
        <div>ChildComponent</div>
    )
});

//父组件
const ParaentCom = () => {
    const [count, setCount] = useState(0);
    const increment = () => setCount(count + 1);
    return (
        <div>
            <button onClick={increment}>点击加1</button>
            <div>点击次数:{count}</div>
            <ChildCom number={count} />
        </div>
    )
}
export default ParaentCom;

React中memo(),useMemo(),useCallback()的使用(区别,解决了什么问题)_第3张图片
如果props不改变,只使用memo()就可以控制子组件不重新渲染。上面是当传入props的值改变时,我们发现子组件进行了重新渲染,但是我们现在的期望是,即使props的值改变,子组件也不要重新渲染。

解决方法,看代码:

import React, { memo, useMemo,useState  } from "react";
//子组件
const ChildCom = memo(({ number }) => {
    console.log('render child component', number);
    return (
        <div>ChildComponent</div>
    )
});

//父组件
const ParaentCom = () => {
    const [count, setCount] = useState(0);
    const increment = () => setCount(count + 1);
    const number = useMemo(() => (count), []);
    return (
        <div>
            <button onClick={increment}>点击加1</button>
            <div>点击次数:{count}</div>
            <ChildCom number={number} />
        </div>
    )
}
export default ParaentCom;

React中memo(),useMemo(),useCallback()的使用(区别,解决了什么问题)_第4张图片
发现即使子组件中props改变,子组件也没有进行重新渲染。

但是,下面一个期望是,控制props中的某些属性改变,也就是复杂的props,可以使子组件重新渲染,这里就需要useMemo的第二个参数来发挥作用了。

看代码:

import React, { memo, useMemo, useState } from "react";
//子组件
const ChildCom = memo(({ number }) => {
    console.log('render child component', number);
    return (
        <div>ChildComponent</div>
    )
});

//父组件
const ParaentCom = () => {
    const changeName = () => setName(name + 1);
    const changeAge = () => setAge(age + 1);
    const [name, setName] = useState('tom');
    const [age, setAge] = useState(20);
    const number = useMemo(() => ({ name, age }), [age]);
    return (
        <div>
            <button onClick={changeAge}>点击修改年龄</button>
            <button onClick={changeName}>点击修改姓名</button>
            <div>姓名:{name}  年龄:{age}</div>
            <ChildCom number={number} />
        </div>
    )
}
export default ParaentCom;

点击修改姓名时:
React中memo(),useMemo(),useCallback()的使用(区别,解决了什么问题)_第5张图片
点击修改年龄时:
React中memo(),useMemo(),useCallback()的使用(区别,解决了什么问题)_第6张图片
从上面两个截图中可以看出,可以这样理解,使用useMemo()方法监听age,从而让我们可以控制子组件什么时候进行重新渲染。但是props如果是方法的话,子组件依然会重新渲染,这里我们就需要useCallback()钩子。

React.useCallback()

先看一段代码:

import React, { memo, useState } from "react";
//子组件
const ChildCom = memo(() => {
    console.log('render child component');
    return (
        <div>ChildComponent</div>
    )
});

//父组件
const ParaentCom = () => {
    const [age, setAge] = useState(20);
    const [count, setCount] = useState(0);
    const increment = () => setCount(count + 1);
    const changeAge = () => setAge(age + 1);
    // const changeAge = useCallback(() => setAge(age + 1), [age])
    return (
        <div>
            <button onClick={increment}>点击加1</button>
            <div> 点击次数:{count}</div>
            <ChildCom onClick={changeAge} />
        </div>
    )
}
export default ParaentCom;

React中memo(),useMemo(),useCallback()的使用(区别,解决了什么问题)_第7张图片

这里由于父组件重新渲染,导致传递的方法也重新渲染,也就是props又重新渲染了,所以子组件又重新渲染了。这种情况下,我们依然不想让子组件重新渲染。

解决方法

import React, { memo, useCallback, useState } from "react";
//子组件
const ChildCom = memo(() => {
    console.log('render child component');
    return (
        <div>ChildComponent</div>
    )
});

//父组件
const ParaentCom = () => {
    const [age, setAge] = useState(20);
    const [count, setCount] = useState(0);
    const increment = () => setCount(count + 1);
    const changeAge = useCallback(() => setAge(age + 1), [])
    return (
        <div>
            <button onClick={increment}>点击加1</button>
            <div> 点击次数:{count}</div>
            <ChildCom onClick={changeAge} />
        </div>
    )
}
export default ParaentCom;

React中memo(),useMemo(),useCallback()的使用(区别,解决了什么问题)_第8张图片
此时点击父组件按钮,控制台就不会打印子组件被渲染的信息了。可以理解为:useCallback()起到了缓存的作用,即使父组件渲染了,useCallback()包裹的函数也不会重新生成,会返回上一次的函数引用。useCallback()钩子的第二个参数,和useMemo,useEffect用法都是一样的,这里就不多介绍。

你可能感兴趣的:(React,react.js,javascript,前端)