当前位置: 首页 > news >正文

步入React正殿 - State进阶

目录

扩展学习资料

State进阶知识点

状态更新扩展

shouldComponentUpdate

PureComponent

为何使用不变数据【保证数据引用不会出错】

 单一数据源

 @/src/App.js

@/src/components/listItem.jsx

状态提升

 @/src/components/navbar.jsx

@/src/components/listPage.jsx

@src/App.js

有状态组件&无状态组件

Stateful【有状态】和Stateless【无状态】的区别

Stateful

Stateless

小结

练习


扩展学习资料

预习资料名称 

链接

备注

不可变数据

https://github.com/immutable-js/immutable-js

JS内存管理

内存管理 - JavaScript | MDN

状态提升

mangojuice.top - 该网站正在出售! - mangojuice 资源和信息。

扩展阅读

context管理状态

http://react.html.cn/docs/context.html  

聊一聊我对 React Context 的理解以及应用 - 掘金

扩展阅读

State进阶知识点

  • 通过条件判断优化渲染
  • 使用不可变数据
  • 状态提升
  • 使用无状态组件

状态更新扩展

阻止不必要的render方法执行

shouldComponentUpdate

// render渲染执行前调用的函数,返回false,可以有效的阻止不必要的render方法执行shouldComponentUpdate(nextProps, nextState) {console.log('props', this.props, nextProps);console.log('state', this.state, nextState);if(this.state.count === nextState.count) {return false}if(this.props.id === nextProps.id) {return false}return true}

PureComponent

import React, { PureComponent } from 'react';
class ListItem extends PureComponent {}

为何使用不变数据【保证数据引用不会出错】

// ...
handleDelete = (id) => {// 使用不可变数据, filter返回一个新数组const listData = this.state.listData.filter((item) => item.id !== id);this.setState({listData,});};handleAmount = () => {// 如不使用新的数组【没有使用不可变数据】, state变化,不会重新渲染UI//const _list = this.state.listData.concat([]);/* pop() 方法用于删除数组的最后一个元素并返回删除的元素。注意:此方法改变数组的长度!提示: 移除数组第一个元素,请使用 shift() 方法。*/_list.pop();this.setState({listData: _list,});};
// ...

如下图,如果没有创建新的引用,在PureComponent中,不会调用render

 如下图,使用不可变数据,可以避免引用带来的副作用,使得整个程序数据变的易于管理

 单一数据源

handleReset = () => {// 使用map方法创建一个新的数组const _list = this.state.listData.map((item) => {// ... 解构符const _item = { ...item };_item.value = 0;return _item;});this.setState({listData: _list,});// 此时props数据变化,子组件state.count没变化// 原因出在没有使用单一数据源};

 @/src/App.js

import React, { PureComponent } from 'react';
import ListItem from './components/listItem';
import ListItemFunc from './components/listItemFunc';
import style from './components/listitem.module.css';// eslint-disable-next-line no-unused-vars
class App extends PureComponent {constructor(props) {super(props);this.state = {listData: [{id: 1,name: 'sony 65寸高清电视',price: 4000,stock: 1,value: 4,},{id: 2,name: '华为 Meta30',price: 6000,stock: 12,value: 2,},{id: 3,name: '华硕 玩家国度笔记本',price: 10000,stock: 11,value: 1,}],};}renderList() {return this.state.listData.map((item) => {return (<ListItemkey={item.id}data={item}onDelete={this.handleDelete}onDecrease={this.handleDecrease}onAdd={this.handleAdd}/>);});}handleDelete = (id) => {// 使用不可变数据, filter返回一个新数组const listData = this.state.listData.filter((item) => item.id !== id);this.setState({listData,});};handleDecrease = (id) => {// 使用不可变数据, filter返回一个新数组const _data = this.state.listData.map((item) => {if (item.id === id) {const _item = { ...item };_item.value--;if (_item.value < 0) _item.value = 0;return _item;}return item;});this.setState({listData: _data,});};handleAdd = (id) => {// 使用不可变数据, filter返回一个新数组console.log(id);const _data = this.state.listData.map((item) => {if (item.id === id) {const _item = { ...item };_item.value++;return _item;}return item;});this.setState({listData: _data,});};handleAmount = () => {// 如不使用新的数组【没有使用不可变数据】, state变化,不会重新渲染UI//const _list = this.state.listData.concat([]);/* pop() 方法用于删除数组的最后一个元素并返回删除的元素。注意:此方法改变数组的长度!提示: 移除数组第一个元素,请使用 shift() 方法。*/_list.pop();this.setState({listData: _list,});};handleReset = () => {// 使用map方法创建一个新的数组const _list = this.state.listData.map((item) => {// ... 结构符const _item = { ...item };_item.value = 0;return _item;});this.setState({listData: _list,});// 此时props数据变化,子组件state.count没变化// 原因出在没有使用单一数据源};render() {return (<div className='container'><button onClick={this.handleAmount} className='btn btn-primary'>减去最后一个</button><button onClick={this.handleReset} className='btn btn-primary'>重置</button>{this.state.listData.length === 0 && (<div className='text-center'>购物车是空的</div>)}{this.renderList()}</div>);}
}export default App;

@/src/components/listItem.jsx

// import React, { Component } from 'react';
import React, { PureComponent } from 'react';
import style from './listitem.module.css';
import classnames from 'classnames/bind';
const cls = classnames.bind(style);
class ListItem extends PureComponent {// 类的构造函数// eslint-disable-next-line no-useless-constructorconstructor(props) {super(props);} render() {console.log('item is rendering');return (<div className='row mb-3'><div className='col-4 themed-grid-col'><span style={{ fontSize: 22, color: '#710000' }}>{this.props.data.name}</span></div><div className='col-1 themed-grid-col'><span className={cls('price-tag')}>¥{this.props.data.price}</span></div><divclassName={`col-2 themed-grid-col${this.props.data.value ? '' : '-s'}`}><buttononClick={() => {this.props.onDecrease(this.props.data.id);}}type='button'className='btn btn-primary'>-</button><span className={cls('digital')}>{this.props.data.value}</span><buttononClick={() => {this.props.onAdd(this.props.data.id);}}type='button'className='btn btn-primary'>+</button></div><div className='col-2 themed-grid-col'>¥ {this.props.data.price * this.props.data.value}</div><div className='col-1 themed-grid-col'><buttononClick={() => {this.props.onDelete(this.props.data.id);}}type='button'className='btn btn-danger btn-sm'>删除</button></div></div>);}
}
export default ListItem;

状态提升

处理组件和子组件数据传递,自顶向下单向流动

 @/src/components/navbar.jsx

import React, { PureComponent } from 'react';
class Nav extends PureComponent {render() {return (<nav className='navbar navbar-expand-lg navbar-light bg-light'><div className='container'><div className='wrap'><span className='title'>NAVBAR</span><span className='badge badge-pill badge-primary ml-2 mr-2'>{this.props.itemNum}</span><buttononClick={this.props.onReset}className='btn btn-outline-success my-2 my-sm-0 fr'type='button'>Reset</button></div></div></nav>);}
}
export default Nav;

@/src/components/listPage.jsx

import React, { PureComponent } from 'react';
import ListItem from './listItem.jsx';
// 商品列表渲染
class ListPage extends PureComponent {renderList() {return this.props.data.map((item) => {return (<ListItemkey={item.id}data={item}onDelete={this.props.handleDelete}onDecrease={this.props.handleDecrease}onAdd={this.props.handleAdd}/>);});}render() {return (<div className='container'>{this.props.data.length === 0 && (<div className='text-center'>购物车是空的</div>)}{this.renderList()}</div>);}
}
export default ListPage;

@src/App.js

import React, { PureComponent } from 'react';
import Nav from './components/navbar';
import ListPage from './components/listPage';
const listData = [{id: 1,name: 'sony 65寸高清电视',price: 4000,stock: 1,value: 4,},{id: 2,name: '华为 Meta30',price: 6000,stock: 12,value: 2,},{id: 3,name: '华硕 玩家国度笔记本',price: 10000,stock: 11,value: 1,},
];
// eslint-disable-next-line no-unused-vars
class App extends PureComponent {constructor(props) {super(props);this.state = {listData: listData,};}handleDelete = (id) => {// 使用不可变数据, filter返回一个新数组const listData = this.state.listData.filter((item) => item.id !== id);this.setState({listData,});};handleDecrease = (id) => {// 使用不可变数据, filter返回一个新数组const _data = this.state.listData.map((item) => {if (item.id === id) {const _item = { ...item };_item.value--;if (_item.value < 0) _item.value = 0;return _item;}return item;});this.setState({listData: _data,});};handleAdd = (id) => {// 使用不可变数据, filter返回一个新数组console.log(id);const _data = this.state.listData.map((item) => {if (item.id === id) {const _item = { ...item };_item.value++;return _item;}return item;});this.setState({listData: _data,});};handleAmount = () => {// 如不使用新的数组【没有使用不可变数据】, state变化,不会重新渲染UI//const _list = this.state.listData.concat([]);/* pop() 方法用于删除数组的最后一个元素并返回删除的元素。注意:此方法改变数组的长度!提示: 移除数组第一个元素,请使用 shift() 方法。*/_list.pop();this.setState({listData: _list,});};handleReset = () => {// 使用map方法创建一个新的数组const _list = this.state.listData.map((item) => {// ... 结构符const _item = { ...item };_item.value = 0;return _item;});this.setState({listData: _list,});// 此时props数据变化,子组件state.count没变化// 原因出在没有使用单一数据源};render() {return (<><Nav itemNum={this.state.listData.length} onReset={this.handleReset} /><ListPagedata={this.state.listData}handleAdd={this.handleAdd}handleAmount={this.handleAmount}handleDecrease={this.handleDecrease}handleDelete={this.handleDelete}handleReset={this.handleReset}/></>);}
}
export default App;

有状态组件&无状态组件

Stateful【有状态】和Stateless【无状态】的区别

Stateful

  • 类组件
  • 有状态组件
  • 容器组件

Stateless

  • 函数组件
  • 无状态组件
  • 展示组件

尽可能通过状态提升原则,将需要的状态提取到父组件中,而其他的组件使用无状态组件编写【父组件有状态,子组件无状态】

无状态组件简单好维护,单一从上而下的数据流

小结

  • 优化渲染
  • 使用不可变数据
  • 单一数据源以及状态提升
  • 无状态组件写法

练习

【题目1】 用单一数据源原则和状态提升原则改造购物车工程

【题目2】 目前Header中显示的是商品种类数量,改造成商品的总数目

相关文章:

步入React正殿 - State进阶

目录 扩展学习资料 State进阶知识点 状态更新扩展 shouldComponentUpdate PureComponent 为何使用不变数据【保证数据引用不会出错】 单一数据源 /src/App.js /src/components/listItem.jsx 状态提升 /src/components/navbar.jsx /src/components/listPage.jsx src/A…...

【QT+ffmpeg】QT+ffmpeg 环境搭建

1.qt下载地址 download.qt.io/archive/ 2. win10sdk 下载 https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/ 安装 debug工具路径 qtcreater会自动识别 调试器选择...

责任链模式解决多个ifelse问题

责任链定义 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许多个对象按照顺序处理请求&#xff0c;直到其中一个对象能够处理该请求为止。 在责任链模式中&#xff0c;通常有一个抽象处理者&#xff08;Handler&a…...

Lnton羚通关于PyTorch的保存和加载模型基础知识

SAVE AND LOAD THE MODEL (保存和加载模型) PyTorch 模型存储学习到的参数在内部状态字典中&#xff0c;称为 state_dict, 他们的持久化通过 torch.save 方法。 model models.shufflenet_v2_x0_5(pretrainedTrue) torch.save(model, "../../data/ShuffleNetV2_X0.5.pth…...

python+django+mysql项目实践四(信息修改+用户登陆)

python项目实践 环境说明: Pycharm 开发环境 Django 前端 MySQL 数据库 Navicat 数据库管理 用户信息修改 修改用户信息需要显示原内容,进行修改 通过url传递编号 urls views 修改内容需要用数据库的更新,用update进行更新,用filter进行选择 输入参数多nid,传递要修…...

sCrypt编程马拉松于8月13日在复旦大学成功举办

继6月在英国Exeter大学成功举办了为期一周的区块链编程马拉松后&#xff0c;美国sCrypt公司创始人兼CEO刘晓晖博士带领核心团队成员王一强、郑宏锋、周全&#xff0c;于8月13日在复旦大学再次成功举办了一场全新的sCrypt编程马拉松。 本次活动由上海可一澈科技有限公司与复旦大…...

Selenium手动和自动两种方式启动Chrome驱动

1. 自动启动chrome驱动(已经安装了Selenium库和Chrome驱动) 要使用Selenium自动跟随自带的Chrome驱动&#xff0c;你需要首先确保你已经安装了Selenium库和Chrome驱动。然后&#xff0c;你可以按照以下步骤进行操作&#xff1a; 导入必要的库&#xff1a; from selenium imp…...

《PostgreSQL 开发指南》第32篇 物化视图

物化视图概述 物化视图&#xff08;Materialized View&#xff09;是 PostgreSQL 提供的一个扩展功能&#xff0c;它是介于视图和表之间的一种对象。 物化视图和视图的最大区别是它不仅存储定义中的查询语句&#xff0c;而且可以像表一样存储数据。物化视图和表的最大区别是它…...

【RocketMQ】快速入门

文章目录 消费模式同步消息异步消息单向消息延迟消息批量消息顺序消息事务消息Tag标签和Key键Tag的使用Key的使用 首先引入rocketmq的依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-client</artifactId><ve…...

AB跳转轮询:让你的独立站收款智能化

独立站在近两年成为跨境电商的热门布局之一&#xff0c;特别是在亚马逊封号潮后&#xff0c;许多卖家开始转向独立站运营。然而&#xff0c;在迅速发展的同时&#xff0c;也不可避免地出现了一些问题&#xff0c;比如很多卖家的资金经常被不同程度地冻结&#xff0c;好不容易出…...

所有用户都能使用sudo吗

是的&#xff0c;Linux系统中的普通用户可以通过配置访问 sudo 命令来获得超级用户&#xff08;root&#xff09;权限的临时访问权。这使得普通用户可以在需要时执行需要管理员权限的操作&#xff0c;而无需永久性地切换到超级用户账户。 通过 sudo 命令&#xff0c;系统管理员…...

【广州华锐视点】VR警务教育实训系统模拟真实场景进行实践训练

随着科技的发展&#xff0c;虚拟现实技术在教育领域得到了广泛的应用。VR警务教育实训系统就是其中的一种应用&#xff0c;该系统由广州华锐互动开发&#xff0c;可以模拟真实的警务场景&#xff0c;让学生通过虚拟现实技术进行实践训练&#xff0c;提高学生的实践能力和技能水…...

【深入浅出C#】章节 7: 文件和输入输出操作:处理文本和二进制数据

文件和输入输出操作在计算机编程中具有重要性&#xff0c;因为它们涉及数据的持久化存储和交互。数据可以是不同类型的&#xff0c;例如文本、图像、音频、视频和二进制数据。这些不同类型的数据具有不同的存储需求。 文本数据是最常见的数据类型之一&#xff0c;用于存储和传输…...

Matlab中图例的位置(图例放在图的上方、下方、左方、右方、图外面)等

一、图例默认位置 默认的位置在NorthEast r 10; a 0; b 0; t0:0.1:2.1*pi; xar*cos(t); ybr*sin(t); A1plot(x,y,r,linewidth,4);%圆 hold on axis equal A2plot([0 0],[1 10],b,linewidth,4);%直线 legend([A1,A2],圆形,line)二、通过Location对legend的位置进行改变 变…...

【算法学习】两数之和II - 输入有序数组

题目描述 原题链接 给你一个下标从 1 开始的整数数组 numbers &#xff0c;该数组已按 非递减顺序排列 &#xff0c;请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] &#xff0c;则 1 < index1 < …...

聚观早报|京东称在技术投入没有止境;木蚁机器人完成B2轮融资

【聚观365】8月18日消息 京东零售CEO表示在技术上投入没有止境 木蚁机器人完成B2轮超亿元融资 耐能推出AI芯片KL730 三星电子泰勒晶圆厂首家客户是AI半导体厂商 韩国新能源汽车7月出口额同比大增36% 京东零售CEO表示在技术上投入没有止境 近日&#xff0c;京东零售CEO辛利…...

C语言:选择+编程(每日一练)

目录 选择题&#xff1a; 题一&#xff1a; 题二&#xff1a; 题三&#xff1a; 题四&#xff1a; 题五&#xff1a; 编程题&#xff1a; 题一&#xff1a;尼科彻斯定理 示例1 题二&#xff1a;等差数列 示例2 本人实力有限可能对一些地方解释和理解的不够清晰&…...

信道数据传输速率、码元传输速率、调制速度,信号传播速度之间的关系

1、信道数据传输速率&#xff08;bit/s&#xff09; 举例&#xff1a;移动通信中的数据传输速率。假设你的手机连接到4G网络&#xff0c;该网络的最大理论数据传输速率为100 Mbps。这意味着在理想情况下&#xff0c;你的手机可以以每秒100兆比特的速度传输数据。 2、码元传输速…...

docker的使用方法总结

Docker是一个非常强大的工具&#xff0c;它可以用于创建、部署和运行应用程序。以下是一些docker相关的常用指令&#xff0c; 1、查看docker版本 docker version 2、查看正在运行的Docker容器 docker ps 3、查看所有的docker容器&#xff08;包括没有运行的容器&#xff0…...

【C#】条码管理操作手册

前言&#xff1a;本文档为条码管理系统操作指南&#xff0c;介绍功能使用、参数配置、资源链接&#xff0c;以及异常的解决等。思维导图如下&#xff1a; 一、思维导图 二、功能操作–条码打印&#xff08;客户端&#xff09; 2.1 参数设置 功能介绍&#xff1a;二维码图片样…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...

五子棋测试用例

一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏&#xff0c;有着深厚的文化底蕴。通过将五子棋制作成网页游戏&#xff0c;可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家&#xff0c;都可以通过网页五子棋感受到东方棋类…...