React@16.x(15)PureComponent 和 memo
目录
- 1,什么是 PureComponent
- 2,什么是 memo
- 3,举例
- 3.2,优化1
- 3.1,优化2-函数位置
- 4,注意点
- 4.1,为了提升效率,应该尽量使用 `PureComponent`
- 4.2,不要直接改变之前的状态,而是覆盖
- 4.3,为什么不进行深比较?
1,什么是 PureComponent
纯组件,为了避免不必要的渲染(运行 render)来提升效率。
优化思路:如果一个组件的状态和属性都没有变化,那就不用重新渲染。
具体实现:当某个组件 extends PureComponent 时,则该组件的 shouldComponentUpdate 中会对新旧属性和状态进行浅比较,如果都相等则不会重新渲染(return false)。
// 原理大致如下:
import React, { Component } from "react";export default class Task extends Component {shouldComponentUpdate(nextProps, nextState) {if (isEqual(this.props, nextProps) && isEqual(this.state, nextState)) {return false;}return true;}render() {}
}const isEqual = (obj1, obj2) => {for (const key in obj1) {if (obj1[key] !== obj2[key]) {return false}}return true
}
效果等于:
import React, { PureComponent } from "react";export default class Task extends PureComponent {render() {}
}
2,什么是 memo
React.memo 是一个HOC,相当于给函数组件套了一个 PureComponent,让函数组件也能进行优化。
// 大致原理:
import React, { PureComponent } from "react";function memo(funcComp) {return class Memo extends PureComponent {render() {return <>{funcComp(this.props)}</>}};
}
3,举例
一个展示列表的组件,逻辑很简单,结构如下:
-- TaskContainer-- TaskList-- Task-- TaskAdd(一个输入框,可以新增列表项)
TaskContainer(console.log("container render");)
import React, { Component } from "react";
import TaskAdd from "./TaskAdd";
import TaskList from "./TaskList";export default class TaskContainer extends Component {state = {list: Array.from(new Array(10)).map((item, index) => `任务${index}`),};render() {console.log("container render");return (<div><TaskAddchangeList={(newTask) => {this.setState({list: [...this.state.list, newTask],});}}></TaskAdd><TaskList list={this.state.list}></TaskList></div>);}
}
TaskList(console.log("list render");)
import React, { Component } from "react";
import Task from "./Task";export default class TaskList extends Component {render() {console.log("list render");return (<div>{(this.props.list || []).map((m) => (<Task name={m} key={m}></Task>))}</div>);}
}
TaskAdd(console.log("add render");)
import React, { Component } from "react";export default class TaskAdd extends Component {state = {taskName: "",};render() {console.log("add render");return (<div><inputvalue={this.state.taskName}onChange={(e) => {this.setState({ taskName: e.target.value });}}></input><button onClick={() => this.props.changeList(this.state.taskName)}>添加任务</button></div>);}
}
Task(console.log("task render");)
import React, { Component } from "react";export default class Task extends Component {render() {console.log("task render");return <div>{this.props.name}</div>;}
}
使用:
import React, { Component, Component } from "react";
import TaskContainer from "./components/Pure/TaskContainer";export default class App extends Component {render() {return <TaskContainer></TaskContainer>;}
}
初次渲染时,打印结果:
container render
add render
list render
10次 task render
添加一个列表项时,打印结果:
container render
add render
list render
11次 task render
3.2,优化1
当添加一个列表项时,已经被渲染的10个 task 不应该再次渲染,因为它们自身没有发生任何变化。只应该渲染新增的那一个即可。
此时就可以用到 PureComponent:
export default class Task extends Component {}
// 替换为
export default class Task extends PureComponent {}
这时再添加一个列表项时,在打印结果可以看到 Task 只渲染了一次(新增的)。
container render
add render
list render
task render
3.1,优化2-函数位置
注意到新增列表时,TaskAdd 组件在点击按钮后,不应该再次渲染(不应该打印 add render),因为它的状态和属性也没有发生变化。
也做下替换:
export default class TaskAdd extends Component {}
// 替换为
export default class TaskAdd extends PureComponent {}
发现结果并没有发生变化,add render 依旧会打印。
这时因为在 TaskContainer 中,给组件 TaskAdd 传递的属性 changeList 是直接写在组件上的。这样每次执行 TaskContainer.render() 时,都算作一个新的属性,所以 TaskAdd extends PureComponent 没有生效。
做以下替换即可:
export default class TaskContainer extends Component {render() {return (<div><TaskAddchangeList={(newTask) => {this.setState({list: [...this.state.list, newTask],});}}></TaskAdd></div>);}
}// 替换为
export default class TaskContainer extends Component {changeList = (newTask) => {this.setState({list: [...this.state.list, newTask],});};render() {return (<div><TaskAdd changeList={this.changeList}></TaskAdd></div>);}
}
4,注意点
4.1,为了提升效率,应该尽量使用 PureComponent
4.2,不要直接改变之前的状态,而是覆盖
旧的状态应该是不可变的(immutable),只能通过创建新状态来覆盖之前的状态。
举例,当使用 PureComponent 时,下面的代码不会更新数据。因为进行的是浅比较,此时数组地址相同。
this.state.task.push(xxx)
this.setState({task: this.state.task
})
换成下面的方式就可以了:产生了新数组。
this.setState({task: [...this.state.task, xxx]
})
// 或
this.setState({task: this.state.task.concat(xxx)
})
如果是对象的话:产生了新对象。
this.setState({obj: {...this.state.obj,b: xxx}
})
// 或
this.setState({obj: Object.assign({}, this.state.obj, {b: xxx})
})
4.3,为什么不进行深比较?
因为本来就是为了提升效率,减少 render 执行次数。深比较会比较费时,得不偿失。
以上。
相关文章:
React@16.x(15)PureComponent 和 memo
目录 1,什么是 PureComponent2,什么是 memo3,举例3.2,优化13.1,优化2-函数位置 4,注意点4.1,为了提升效率,应该尽量使用 PureComponent4.2,不要直接改变之前的状态&#…...
[C++11/14新特性] tuple元组介绍
C11 标准新引入了一种类模板,命名为 tuple(中文可直译为元组)。tuple 最大的特点是:实例化的对象可以存储任意数量、任意类型的数据。tuple 的应用场景很广泛,例如当需要存储多个不同类型的元素时,可以使用…...
小熊家务帮day8-day9 客户管理模块2 (用户定位,地址簿,实名认证,银行卡信息上传等功能)
客户管理模块 0.用户定位功能0.1 需求0.2 接口分析0.3 接口开发Controller层开发Service层开发 1.我的地址簿功能1.1 需求1.2 数据库设计1.3 新增地址簿1.3.1 接口设计1.3.2 接口开发Controller层开发Service层开发测试功能 1.4 地址簿查询1.4.1 接口设计1.4.2 接口开发Control…...
amis 事件动作 和 行为按钮 常用用法
行为按钮 action (仅是对click事件的处理) actionType:这是 action 最核心的配置,来指定该 action 的作用类型,支持:ajax、link、url、drawer、dialog、confirm、cancel、prev、next、copy、close。 Butt…...
4K高刷显示器 - 蚂蚁电竞ANT27VU
可以毫不夸张地说,每一局游戏最终能够取得胜利,实际上都与一套极为优秀的电竞 PC 有着紧密的关联,因为其能够提供强大的性能支持与流畅的体验。同样的道理,一套优秀的电竞 PC 若想发挥出最佳的效果,那也都离不开一台能…...
图解支付系统的渠道路由设计
大家好,我是隐墨星辰,今天和大家聊聊渠道路由设计。 这篇文章主要讲清楚:渠道路由是什么,为什么需要渠道路由,渠道路由的几种形态,一个简洁而实用的基于规则的渠道路由设计。 注:有些公司称渠…...
Leecode---347:输出前k个高频元素(使用unordered_map)
题目: 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 思路: 三步: 1、用map来记录每个元素出现的次数 2、按map中的值大小排序,先让其有序,再逆…...
k8s ceph(静态pvc)
1.在 Kubernetes 节点上安装ceph-common 包。这个包包含了连接到 Ceph 集群所需的工具和库。可以使用以下命令在每个节点上安装: sudo apt-get install ceph-common2.在 Kubernetes 中创建一个 Secret 对象,用于存储连接到 Ceph 集群所需的密钥和配置信息…...
Qt QScript 之 C++/JavaScript相互调用
文章目录 Qt Script什么是ECMAScriptQt 中JavaScriptclass 详解Basic UsageQObject对脚本引擎可用使用信号槽connect 三种模式访问属性, 子对象使c++对象可用于用Qt Script编写的脚本C++ 类成员函数可用于脚本C++ 类属性可用于脚本对脚本中的c++对象信号的反应函数对象和本机函…...
可能会引起空指针
PreparedStatement preparedStatement null; preparedStatement conn.prepareStatement(sql); 如果直接下面这个可能会赋值给空指针 因为要在try{}和catch{}里面都用,所以要定义在try外面为null //如果只是测试,可以PreparedStatement preparedStatement conn.prepareSta…...
Linux input输入子系统
Linux input 更多内容可以查看我的github Linux输入子系统框架 Linux输入子系统由驱动层、核心层、事件处理层三部分组成。 驱动层:输入设备的具体驱动程序,负责与具体的硬件设备进行交互,并将底层的硬件输入转化为统一的事件形式ÿ…...
dataworks调度参数
概述 调度参数是DataWorks任务调度时使用的参数,调度参数会根据任务调度的业务时间及调度参数的取值格式自动替换取值,实现在任务调度时间内参数的动态取值。 调度参数通过赋值方式分为自定义参数(推荐)和系统内置变量两大类。 …...
JavaScript第五讲:事件,条件循环语句,错误处理
前言 在编程的世界里,事件、条件和循环语句、以及错误处理是构建任何复杂程序或应用的基石。无论是开发一个简单的网页交互,还是构建一个庞大的企业级系统,这些基础概念都扮演着至关重要的角色。今天星途将通过这篇文章,分别深入…...
BrainGPT1,一个帮你b站点歌放视频的多模态多轮对话模型
BrainGPT1,一个帮你b站点歌放视频的多模态多轮对话模型 返回论文目录 项目地址 模型地址 作者:华东师范大学,计算机科学与技术学院,智能教育研究院的小怪兽会微笑。 介绍 BrainGPT1是一个工具调用多轮对话模型,与G…...
带DSP音效处理D类数字功放TAS5805M中文资料
国产替代D类数字功放中文资料访问下方链接 ACM8628 241W立体声182W单通道数字功放中文寄存器表 内置DSP多种音频处理效果ACM8628M-241W立体声或182W单通道数字功放 1 特性 具有增强处理能力和低功率损耗的 TAS5805M 23W、无电感器、数字输入、立体声、闭环 D 类音频放大器 …...
java中BigDecimal的比较
BigDecimal是Java中的一个类,位于java.math包中,它提供了任意精度的有符号十进制数字的表示,以及对这些数字进行算术运算的方法 BigDecimal的主要用途包括: 1.金融计算:金融领域对数值的精度要求非常高,使…...
张大哥笔记:你卖什么,就反着来卖
普通人打工的一生,就是努力工作,买房,买车,送孩子上好的学校,为了孩子不要输在起跑线上,拼命报各种补习班等,这些都是普通人认为的主流价值观文化,也造就了一批批的赚钱机器…...
Nginx(openresty) 开启gzip压缩功能 提高web网站传输速度
1 开启nginx gzip压缩后,网页的图片,css、js等静态资源的大小会减少,节约带宽,提高传输效率,给用户快的体验,给用户更好的体验. 2 安装 #centos 8.5 yum install gzip 3 配置 #建议统一配置在http段 vim /usr/loca…...
nn.Embedding使用
nn.Embedding使用 Embedding.weight会从标准正态分布中初始化成大小为(num_embeddings, embedding_dim)的矩阵 PE矩阵的作用就是替换这个标准正态分布 input中的标号表示从矩阵对应行获取权重来表示单词 # 1.设置embedding结构 max_seq_len 1000 # 句…...
Qt6 mathgl数学函数绘图
1. 程序环境 Qt6.5.1, mingw11.2mathgl 8.0.1: https://sourceforge.net/projects/mathgl/,推荐下载mathgl-8.0.LGPL-mingw.win64.7z,Windows环境尝试自己编译mathgl会缺失一些库,补充完整也可以自己编译,路径"D:\mathgl-8.0.LGPL-mingw.win64\bin"添加至系统环境…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
