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;

在这里插入图片描述

子组件中有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包裹之后,我们多次点击按钮,发现子组件没有重新渲染。

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;

在这里插入图片描述
如果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;

在这里插入图片描述
发现即使子组件中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;

点击修改姓名时:
在这里插入图片描述
点击修改年龄时:
在这里插入图片描述
从上面两个截图中可以看出,可以这样理解,使用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;

在这里插入图片描述

这里由于父组件重新渲染,导致传递的方法也重新渲染,也就是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;

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

.ToString()°

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

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

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

打赏作者

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

抵扣说明:

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

余额充值