深入学习React函数组件性能优化三剑客useMemo、useCallback、memo

2023-11-14

Hook使用规则

  • 只能在函数的最外层调用Hook,不能在循环、条件判断或子函数中调用。
  • 只能在React函数组件或自定义Hook中调用Hook,不可在其他JavaScript函数中使用。

useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

memo是memoized的简写,意思是缓存;computeExpensiveValue的意思是“计算开销很大的值”。
useMemo的作用是通过存储计算开销很大的值并在再次出现相同输入时返回缓存的结果,以此来加速计算机程序运行效率。

参数

参数1:纯函数

该参数必须是纯函数,即入参相同时输出结果必定相同,该纯函数必须有返回值。
常见的应用场景:例如根据id查名称,在成千上万的id中找对应名字的计算开销很大,非常适合用useMemo做性能优化。

参数2:计算依赖项

即参数1函数所需要的参数。
注意:当计算函数引用依赖项以外的变量时,由于Reac做t异步批量更新可能会造成引用到的值不是最新的。

返回值:计算开销很大的值

usuCallback

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

callback的意思是回调函数;memoizedCallback的意思是缓存的回调函数。
usuCallback的作用是通过存储回调函数,使得该函数在入参不变时保持不变,可以避免重复渲染,优化性能。

参数

参数1:回调纯函数

该函数必须是纯函数,即入参相同输出的结果必定相同。
常见的应用场景:缓存更新数据的函数。

参数2:计算依赖项

即doSomething函数需要的参数。
注意:当计算函数引用依赖项以外的变量时,由于Reac做t异步批量更新可能会造成引用到的值不是最新的。

返回值:回调函数

补充

useCallback常用来和memo配合使用,光用useCallback不搭配memo不能阻止重复渲染。

React.memo

const MyComponent = React.memo(function MyComponent(props) {
  /* 使用 props 渲染 */
});

React.memo是高阶组件,memo会检查参数1中的props变更情况及参数2中的设置来决定组件是否重新渲染。
组件内使用useState、useReducer、useContext等Hook发生变化而引起重新渲染不受memo影响。

参数1:函数组件

参数2:可选

  • 无参数2时:对前后props作浅比较,2个对象内存地址相同就不重新渲染,不同就重新渲染。
  • 有参数2时:参数2必须返回true或false,返回true不重新渲染;返回false重新渲染
    参数2范例:(preProps, nextProps) => preProps.id === nextProps.id

补充

memo参数2的判断逻辑与shouldComponentUpdate()生命周期方法不一样,shouldComponentUpdate()返回true表示可以重新渲染,返回false表示不可重新渲染。

案例:

import React, { memo, useCallback, useMemo, useState } from "react";


const Increase: React.FC<{ add: () => void }> = memo((props) => {
    console.log("Increase")
    return (
        <button onClick={props.add}> + </button>
    )
})

const Decrease: React.FC<{ sub: () => void }> = (props) => {
    console.log("Decrease")
    return (
        <button onClick={props.sub}> - </button>
    )
}

const Order: React.FC<{
    item: {
        id: number,
        goodsId: number,
        height: number,
        userName: string,
    },
    goodsArr: { id: number, name: string }[],
    setOrderList: React.Dispatch<React.SetStateAction<{
        id: number;
        goodsId: number;
        height: number;
        userName: string;
    }>>,
}> = (props) => {
    const { item, goodsArr, setOrderList } = props
    const showName = useMemo(
        () => {
            console.log("计算了商品名")
            for (const i of goodsArr) {
                if (i.id === item.goodsId) {
                    return i.name
                }
            }
            return ""
        },
        // eslint-disable-next-line react-hooks/exhaustive-deps
        [item.goodsId]  // 正确的依赖项
        // [item]  // 错误的依赖项
    )
    const add = useCallback(() => setOrderList(preState => ({ ...preState, height: preState.height + 1 })), [])
    const sub = useCallback(() => setOrderList(preState => ({ ...preState, height: preState.height > 0 ? preState.height - 1 : 0 })), [])

    return (
        <div>购买人:{item.userName},品名:{showName},重量:{item.height}<Increase add={add} /> <Decrease sub={sub} /></div>
    )

}


const App = () => {
    const [orderList, setOrderList] = useState({ id: 1, goodsId: 1, height: 2, userName: "张三" })
    const goodsArr = [
        { id: 1, name: "香蕉", },
        { id: 2, name: "苹果", },
        { id: 3, name: "桃子", },
        { id: 4, name: "葡萄", },
        { id: 5, name: "樱桃", },
    ]
    return (
        <div>
            <Order item={orderList} goodsArr={goodsArr} setOrderList={setOrderList} />
        </div>
    )
}

export default App

代码表现如下:
在这里插入图片描述
在线体验

讲解

useMemo

注意Order组件中showName用到了useMemo,这里存储了商品名称,假设商品列表有上万条记录,那每一次根据id找name都是巨大的开销,如果不做优化,那么点击 + 或 - 按钮响应速度会很慢,给用户的体验就是卡顿。做了优化以后,点击 + 或 - 按钮showName中的console.log(“计算了商品名”)没有打印日志。
使用useMemo的难点在于第二个参数,要设置正确,上面代码注释了一条错误的依赖项,有兴趣的可以看看注释正确的依赖项打开错误的依赖项时运行的日志。

useCallback

注意看代码中的Increase和Decrease组件,这2个组件对应的按钮都绑定类对state做更新的回调函数。

memo

注意看上图中的日志,无论点击了增加按钮或减少按钮,Decrease组件都会重新渲染。差别就是Decrease组件没有使用memo。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入学习React函数组件性能优化三剑客useMemo、useCallback、memo 的相关文章

  • ES6基础详解

    文章目录 ES6理解 1 let和const 2 解构赋值 3 promise 4 async和await 5 Set和Map 6 箭头函数 7 函数的扩展 8 扩展运算符 ES6理解 当问到ES6时 通常指的是JavaScript的ECM
  • 第五章 创建自定义窗口部件

    对已经存在的Qt窗口进行子类化或者直接对QWidget子类化可以快速创建自己的自定义窗口部件 一 自定义窗口部件 十六进制的QSpinBox 本来QSpinBox仅支持十进制数据的 现在子类化接收并显示十六进制数值 头文件 hexspinb
  • 【Flutter入门教程】从零构建电商应用(一)

    在这个系列中 我们将学习如何使用google的移动开发框架flutter创建一个电商应用 本文是flutter框架系列教程的第一部分 将学习如何安装Flutter开发环境并创建第一个Flutter应用 并学习Flutter应用开发中的核心概

随机推荐

  • 图像区域特征

    以 Halcon 里支持的 Region 特征为基础 做概念总结 形状特征 1 圆度 Circularity 衡量一个形状接近圆的程度 取值为 0 1 Circularity 区域面积 外接圆半径2 Circularity frac 213
  • High-Fidelity Pose and Expression Normalization for Face Recognition in the Wild

    CVPR 2015 Matlab code http www cbsr ia ac cn users xiangyuzhu projects HPEN main htm 中科院关于 人脸图像预处理 姿态和表情的归一化 算法的整体流程图如下所
  • crictl使用总结

    crictl 是 CRI 兼容的容器运行时命令行接口 crictl 是 CRI 兼容的容器运行时命令行接口 你可以使用它来检查和调试 Kubernetes 节点上的容器运行时和应用程序 crictl 和它的源代码在 cri tools 代码
  • 常用的Buck型DC-DC的原理图电路

    常用DC DC buck原理图电路 下图是比较完整的DC DC电路设计 全文将主要介绍各个元件的作用 针对该电路各位号分析 1 Vin的C1 C2主要是滤波 使得DC DC芯片输入能够得到较为干净的电 2 R1 R2是限流用的 一般是K级的
  • 2.信号和槽

    MyPushBotton mybtn new MyPushBotton mybtn gt setParent this mybtn gt setText 我自己的按钮 mybtn gt move 200 200 mybtn gt show
  • 数据结构 图的应用

    文章目录 生成树 定义 性质 带权图的最小生成树 最小生成树的生成规则 最小生成树 Kruskal算法 步骤 最小生成树 Prim算法 步骤 最短路径 非负权值的单源最短路径 Dijkstra算法 目的 算法 存储空间 算法步骤 算法实现
  • [Python人工智能] 十.Tensorflow+Opencv实现CNN自定义图像分类案例及与机器学习KNN图像分类算法对比

    从本专栏开始 作者正式开始研究Python深度学习 神经网络及人工智能相关知识 前一篇详细讲解了gensim词向量Word2Vec安装 基础用法 并实现 庆余年 中文短文本相似度计算及多个案例 很幸运被CSDN推荐至封面 本篇文章主要通过T
  • centos8在线文档编辑ONLYOFFICE安装

    第一件事情肯定是安装docker拉 这个在centos8上有点不一样 好在执行代码都一毛一样 root localhost dnf install y podman 上次元数据过期检查 2 02 46 前 执行于 2022年03月31日 星
  • Vue3+TS项目中element-plus自动导入组件后,找不到文件

    问题 原因 从报错代码来看 这是一个ts错误 而且是找不到名称 是没有将 d ts文件加入到tsconfig json配置文件中 所以Typescript还不认识它们 解决 找到项目根目录下 tsconfig json配置文件 includ
  • varchar与char的区别

    1 定长 VS 变长 char表示定长 长度固定 varchar表示变长 即长度可变 当插入数据的长度超过定义的长度时 如果数据库是严格模式 则会拒绝插入并提示错误信息 如果是宽松模式 则会截取然后插入 如果插入的字符串长度小于定义长度时如
  • mac如何彻底卸载Conda

    1 由于Anaconda的安装文件都包含在一个目录中 所以直接将该目录删除即可 删除整个Anaconda目录2 使用Anaconda Clean工具删除 安装Anaconda Clean 在终端输入并回车 conda install ana
  • 基于模拟退火算法与遗传算法的避障路径规划问题求解(Matlab代码)

    基于模拟退火算法与遗传算法的避障路径规划问题求解 Matlab代码 在本文中 我们将探讨如何使用模拟退火算法 Simulated Annealing 和遗传算法 Genetic Algorithm 相结合来解决避障路径规划问题 我们将提供相
  • Meta推出Code Llama:编程的未来已来

    今天 Meta推出了一项前沿技术 Code Llama 这是一款基于Llama 2构建的大型语言模型 LLM 专门用于生成和讨论代码 这一创新技术的发布标志着编程领域的新时代 Code Llama的亮点 先进的LLM Code Llama是
  • 穿特步的程序员教你如何找女朋友

    生活中我们常常发现很多程序员拿着高薪 却常常沦为单身狗 每当情人节来临 却只能形单影只的一个人 过得十分凄惨 自从程序员毕业出来工作进入 IT 行业之后 常常接触不到女性 一不小心就到了被催恋催婚的年纪 前有阿里的高级工程师 穿着一双特步的
  • 不平衡数据分类算法介绍与比较

    介绍 在数据挖掘中 经常会存在不平衡数据的分类问题 比如在异常监控预测中 由于异常就大多数情况下都不会出现 因此想要达到良好的识别效果普通的分类算法还远远不够 这里介绍几种处理不平衡数据的常用方法及对比 符号表示 记多数类的样本集合为L 少
  • 跟着(肆十二)学习——Yolov5训练自己的目标检测模型

    目录 1 环境配置 1 1安装Pycharm 1 2安装anaconda 1 3安装所需要的包 1 3 1安装pytorch 1 3 2安装其他依赖包 1 3 3pycocotools的安装 1 3 4测试一下 2 数据标注 2 1直接在代
  • 阮一峰老师的ES6入门:Generator 函数的语法

    Generator 函数的语法 1 简介 基本概念 Generator 函数是 ES6 提供的一种异步编程解决方案 语法行为与传统函数完全不同 本章详细介绍 Generator 函数的语法和 API 它的异步编程应用请看 Generator
  • 【内存拷贝函数:memcpy与memmove】

    文章目录 前言 一 memcpy函数 内存拷贝 的实验 二 memcpy函数的模拟实现 三 memmove函数 内存移动 的实验 四 memmove函数的模拟实现 总结 前言 c和c 使用的内存拷贝函数 memcpy函数和memmove函数
  • MySQL:sending data状态包含了什么

    MySQL sending data状态包含了什么 一 问题由来 这是一个朋友问我的 成都 麦涩可 原问题如下 数据库发送数据给客户端这个时间算是sql的执行时间嘛 要解决问题我们需要知道MySQL何时将数据传输给了客户端 既然是要传输实际
  • 深入学习React函数组件性能优化三剑客useMemo、useCallback、memo

    Hook使用规则 只能在函数的最外层调用Hook 不能在循环 条件判断或子函数中调用 只能在React函数组件或自定义Hook中调用Hook 不可在其他JavaScript函数中使用 useMemo const memoizedValue