Web学习笔记-React(组合Components)
笔记内容转载自 AcWing 的 Web 应用课讲义,课程链接:AcWing Web 应用课。
CONTENTS
- 1. 创建父组件
- 2. 从上往下传递数据
- 3. 传递子节点
- 4. 从下往上调用函数
- 5. 兄弟组件间传递消息
- 6. 无状态函数组件
- 7. 组件的生命周期
 
本节内容是组件与组件之间的组合,例如用不同组件构成 DOM 树,以及给不同的组件传递数据或者调用不同组件的方法,还有不同组件的生命周期。
1. 创建父组件
我们还是继续在之前的 Box 组件上进行操作,首先创建一个 Boxes 组件,其中包含一系列 Box 组件。
在 components 目录中创建 boxes.jsx:
import React, { Component } from 'react';class Boxes extends Component {state = {  } render() { return (<h1>Boxes</h1>);}
}export default Boxes;
然后修改一下 index.js:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import 'bootstrap/dist/css/bootstrap.css';
import Boxes from './components/boxes';const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Boxes />);
现在我们在 Boxes 中加入多个 Box,当一个组件中包含多个并列元素的时候,需要用一个标签将他们括起来,可以使用 React 中的一个虚拟标签 <React.Fragment>:
import React, { Component } from 'react';
import Box from './box';class Boxes extends Component {state = {  } render() { return (<React.Fragment><Box /><Box /><Box /></React.Fragment>);}
}export default Boxes;
为了方便也可以用一个数组来表示,将 Box 的信息存到 state 里,由于 React 组件如果有若干个儿子那么他们的 key 需要不一样,因此还需要存一个唯一的 id:
import React, { Component } from 'react';
import Box from './box';class Boxes extends Component {state = { boxes: [{id: 1, x: 0},{id: 2, x: 0},{id: 3, x: 0},{id: 4, x: 0},]} render() { return (<React.Fragment>{this.state.boxes.map(box => (<Box key={box.id} />))}</React.Fragment>);}
}export default Boxes;
2. 从上往下传递数据
通过 this.props 属性可以从上到下传递数据。例如我们在 Boxes 中传递 x:
...class Boxes extends Component {state = { ...} render() { return (<React.Fragment>{this.state.boxes.map(box => (<Box key={box.id} x={box.x} name='yyj' />))}</React.Fragment>);}
}export default Boxes;
可以在 Box 中输出信息 console.log(this.props); 查看内容:

修改 Box 中的 x:
import React, { Component } from 'react';  // 输入imrc即可补全class Box extends Component {  // 输入cc即可补全state = { x: this.props.x,};...
}export default Box;
3. 传递子节点
可以将标签写成 <Box></Box> 的形式,然后在标签中添加子标签:
import React, { Component } from 'react';
import Box from './box';class Boxes extends Component {state = { boxes: [{id: 1, x: 0},{id: 2, x: 0},{id: 3, x: 0},{id: 4, x: 0},]} render() { return (<React.Fragment>{this.state.boxes.map(box => (<Box key={box.id} x={box.x} name='yyj'><h1>Title</h1></Box>))}</React.Fragment>);}
}export default Boxes;
这样 this.props 中会多一个属性 children,可以使用 [] 单独访问某个子标签。我们可以将这个传过来的值定义在任何地方,例如可以放到每个 Box 组件的最上方:
import React, { Component } from 'react';  // 输入imrc即可补全class Box extends Component {  // 输入cc即可补全state = { x: this.props.x,};handleClickLeft = (step) => {this.setState({x: this.state.x - step});}handleClickRight = (step) => {this.setState({x: this.state.x + step});}handleClickLeftTmp = () => {this.handleClickLeft(10);}render() {  // Component类的函数,用来返回当前组件最后渲染的HTML结构是什么console.log(this.props);return (// HTML标签中可以使用{}写一个表达式<React.Fragment>{this.props.children}<div style={this.getStyles()}>{this.state.x}</div><button onClick={this.handleClickLeftTmp} className='btn btn-primary m-2'>Left</button><button onClick={() => this.handleClickRight(10)} className='btn btn-success m-2'>Right</button></React.Fragment>);}getStyles() {...}
}export default Box;
4. 从下往上调用函数
父元素可以通过 this.props 向子元素传递信息,子元素也可以使用函数向父元素传递信息。假设我们需要实现通过点击删除按钮删除某个 Box,其信息保存在 Boxes 的 state 中,但是我们点击触发事件是在 Box 中(注意:每个组件的 this.state 只能在组件内部修改,不能在其他组件内修改)。
我们可以在父元素中定义好函数,然后将函数传给子元素:
import React, { Component } from 'react';
import Box from './box';class Boxes extends Component {state = { boxes: [{id: 1, x: 0},{id: 2, x: 0},{id: 3, x: 0},{id: 4, x: 0},]}handleDelete = (boxId) => {// 遍历一遍state.boxes,将box.id不为传入的参数boxId的数据保留下来const res = this.state.boxes.filter(box => box.id !== boxId);this.setState({boxes: res});}render() { return (<React.Fragment>{this.state.boxes.map(box => (<Box key={box.id} id={box.id} x={box.x} name='yyj'onDelete={this.handleDelete}/>))}</React.Fragment>);}
}export default Boxes;
这样子元素就能调用函数对父元素进行操作了:
import React, { Component } from 'react';  // 输入imrc即可补全class Box extends Component {  // 输入cc即可补全...render() {  // Component类的函数,用来返回当前组件最后渲染的HTML结构是什么console.log(this.props);return (// HTML标签中可以使用{}写一个表达式<React.Fragment>...<button onClick={() => this.props.onDelete(this.props.id)} className='btn btn-danger m-2'>Delete</button></React.Fragment>);}getStyles() {...}
}export default Box;
现在我们在 Boxes 中实现一个 Reset 按钮实现清空所有 Box 的 x:
import React, { Component } from 'react';
import Box from './box';class Boxes extends Component {state = { boxes: [{id: 1, x: 0},{id: 2, x: 1},{id: 3, x: 2},{id: 4, x: 3},]}handleDelete = (boxId) => {...}handleReset = () => {const res = this.state.boxes.map(box => {return {id: box.id,x: 0,}});this.setState({boxes: res});}render() {console.log(this.state.boxes);return (<React.Fragment><buttononClick={this.handleReset}style={{marginBottom: '15px'}}className='btn btn-info'>Reset</button>{this.state.boxes.map(box => (<Box key={box.id} id={box.id} x={box.x} name='yyj'onDelete={this.handleDelete}/>))}</React.Fragment>);}
}export default Boxes;
在控制台观察时可以发现点击 Reset 按钮后 x 确实置零了,但是 Box 显示出来的 x 并没有改变,这是因为 state 值不能在外部修改,因此我们可以将 Box 中的 state 删掉,需要在该组件中渲染外面的 state 的值。
每个维护的数据仅能保存在一个 this.state 中,不要直接修改 this.state 的值,因为 setState 函数可能会将修改覆盖掉。
修改 Boxes,将之前 Box 中操作 state 的函数转移过来:
import React, { Component } from 'react';
import Box from './box';class Boxes extends Component {state = { boxes: [{id: 1, x: 0},{id: 2, x: 1},{id: 3, x: 2},{id: 4, x: 3},]}handleDelete = (boxId) => {// 遍历一遍state.boxes,将box.id不为传入的参数boxId的数据保留下来const res = this.state.boxes.filter(box => box.id !== boxId);this.setState({boxes: res});}handleReset = () => {const res = this.state.boxes.map(box => {return {id: box.id,x: 0,}});this.setState({boxes: res});}// 需要知道修改的是哪个boxhandleClickLeft = (box) => {const boxes = [...this.state.boxes];  // 浅拷贝一份const k = boxes.indexOf(box);  // 传入的box是引用,找出其在boxes中的下标kboxes[k] = {...boxes[k]};  // 再clone一遍,相当于创建新的state,深拷贝boxes[k].x--;this.setState({boxes: boxes});}handleClickRight = (box) => {const boxes = [...this.state.boxes];const k = boxes.indexOf(box);boxes[k] = {...boxes[k]};boxes[k].x++;this.setState({boxes: boxes});}render() {return (<React.Fragment><buttononClick={this.handleReset}style={{marginBottom: '15px'}}className='btn btn-info'>Reset</button>{this.state.boxes.map(box => (<Box key={box.id} id={box.id} x={box.x} name='yyj'onDelete={this.handleDelete}onClickLeft={() => this.handleClickLeft(box)}onClickRight={() => this.handleClickRight(box)}/>))}</React.Fragment>);}
}export default Boxes;
然后修改 Box,将 this.state 替换成父组件传递过来的 props:
import React, { Component } from 'react';  // 输入imrc即可补全class Box extends Component {  // 输入cc即可补全render() {  // Component类的函数,用来返回当前组件最后渲染的HTML结构是什么return (// HTML标签中可以使用{}写一个表达式<React.Fragment><div style={this.getStyles()}>{this.props.x}</div><button onClick={this.props.onClickLeft} className='btn btn-primary m-2'>Left</button><button onClick={this.props.onClickRight} className='btn btn-success m-2'>Right</button><button onClick={() => this.props.onDelete(this.props.id)} className='btn btn-danger m-2'>Delete</button></React.Fragment>);}getStyles() {let styles = {width: '50px',height: '50px',backgroundColor: 'lightblue',color: 'white',textAlign: 'center',lineHeight: '50px',borderRadius: '5px',position: 'relative',left: this.props.x};if (this.props.x === 0) {styles.backgroundColor = 'orange';}return styles;}
}export default Box;
5. 兄弟组件间传递消息
如果组件的结构关系更为复杂,那么就需要将多个组件共用的数据存放到最近公共祖先的 this.state 中。
我们创建一个 App 组件,其包含两个子组件 NavBar(导航栏)和 Boxes,这两个组件为兄弟组件。
首先是 navbar.jsx:
import React, { Component } from 'react';class NavBar extends Component {state = {  } render() { return (<nav className="navbar bg-body-tertiary"><div className="container-fluid"><a className="navbar-brand" href="/">Navbar</a></div></nav>);}
}export default NavBar;
然后是 app.jsx:
import React, { Component } from 'react';
import NavBar from './navbar';
import Boxes from './boxes';class App extends Component {state = {  } render() { return (<React.Fragment><div className='container'><NavBar /><Boxes /></div></React.Fragment>);}
}export default App;
现在假设我们要在 NavBar 中存放 Boxes 中有几个 Box 的信息,那么只能把信息放到这两个组件的最近公共祖先 App 中。
我们将 Boxes 中与 state 有关的内容都移到 App 中:
import React, { Component } from 'react';
import Box from './box';class Boxes extends Component {render() {return (<React.Fragment><buttononClick={this.props.onReset}style={{marginBottom: '15px'}}className='btn btn-info'>Reset</button>{this.props.boxes.map(box => (<Box key={box.id} id={box.id} x={box.x} name='yyj'onDelete={this.props.onDelete}onClickLeft={() => this.props.onClickLeft(box)}onClickRight={() => this.props.onClickRight(box)}/>))}</React.Fragment>);}
}export default Boxes;
移动后的 App 如下:
import React, { Component } from 'react';
import NavBar from './navbar';
import Boxes from './boxes';class App extends Component {state = { boxes: [{id: 1, x: 0},{id: 2, x: 1},{id: 3, x: 2},{id: 4, x: 3},]}handleDelete = (boxId) => {// 遍历一遍state.boxes,将box.id不为传入的参数boxId的数据保留下来const res = this.state.boxes.filter(box => box.id !== boxId);this.setState({boxes: res});}handleReset = () => {const res = this.state.boxes.map(box => {return {id: box.id,x: 0,}});this.setState({boxes: res});}// 需要知道修改的是哪个boxhandleClickLeft = (box) => {const boxes = [...this.state.boxes];  // 浅拷贝一份const k = boxes.indexOf(box);  // 传入的box是引用,找出其在boxes中的下标kboxes[k] = {...boxes[k]};  // 再clone一遍,相当于创建新的state,深拷贝boxes[k].x--;this.setState({boxes: boxes});}handleClickRight = (box) => {const boxes = [...this.state.boxes];const k = boxes.indexOf(box);boxes[k] = {...boxes[k]};boxes[k].x++;this.setState({boxes: boxes});}render() { return (<React.Fragment><div className='container'><NavBarboxesCount={this.state.boxes.length}  // 将长度传给NavBar/><Boxesboxes={this.state.boxes}onReset={this.handleReset}onClickLeft={this.handleClickLeft}onClickRight={this.handleClickRight}onDelete={this.handleDelete}/></div></React.Fragment>);}
}export default App;
现在即可在 NavBar 中读取 Boxes 的长度信息了:
import React, { Component } from 'react';class NavBar extends Component {state = {  } render() { return (<nav className="navbar bg-body-tertiary"><div className="container-fluid"><a className="navbar-brand" href="/">Navbar <span>Boxes Count: {this.props.boxesCount}</span></a></div></nav>);}
}export default NavBar;
6. 无状态函数组件
当组件中没有用到 this.state 时,可以简写为无状态的函数组件。类相对于函数最大的好处就是可以很方便地维护状态(局部变量)。
无状态函数组件(Stateless Funtion Component),输入 sfc 即可自动补全出来。函数组件相当于只有 render 函数的类组件。注意:函数的传入参数为 props 对象:
import React from 'react';const NavBar = (props) => {return (<nav className="navbar bg-body-tertiary"><div className="container-fluid"><a className="navbar-brand" href="/">Navbar <span>Boxes Count: {props.boxesCount}</span></a></div></nav>);
}export default NavBar;
7. 组件的生命周期
- Mount周期(挂载,表示对象被创建出来),执行顺序(按顺序执行三个函数):- constructor() -> render() -> componentDidMount()
- Update周期(修改),执行顺序:- render() -> componentDidUpdate()
- Unmount周期(删除),执行顺序:- componentWillUnmount()
相关文章:
 
Web学习笔记-React(组合Components)
笔记内容转载自 AcWing 的 Web 应用课讲义,课程链接:AcWing Web 应用课。 CONTENTS 1. 创建父组件2. 从上往下传递数据3. 传递子节点4. 从下往上调用函数5. 兄弟组件间传递消息6. 无状态函数组件7. 组件的生命周期 本节内容是组件与组件之间的组合&#…...
 
【strstr函数的介绍和模拟实现——超详细版】
strstr函数的介绍和模拟实现 strstr函数的介绍 资源来源于cplusplus网站 strstr函数声明: char *strstr( const char *str1, const char *str2 ); 它的作用其实就是: 在字符串str1中查找是否含有字符串str2,如果存在,返回str2在…...
 
【Terraform】Terraform自动创建云服务器脚本
Terraform 是由 HashiCorp 创建的开源“基础架构即代码”工具 (IaC) 使用HCL(配置语言)描述云平台基础设施(这里教你使用低级基础设施:交换机、云服务器、VPC、带宽) Terraform提供者…...
 
TCP机制之确认应答及超时重传
TCP因为其可靠传输的特性被广泛使用,这篇博客将详细介绍一下TCP协议是如何保证它的可靠性的呢?这得主要依赖于其确认应答及超时重传机制,同时三次握手四次挥手也起到了少部分不作用,但是主要还是由确认应答和超时重传来决定的;注意:这里的可靠传输并不是说100%能把数据发送给接…...
Openharmony3.2 源码编译(ubuntu 22.04) 过程记录
OS: ubuntu 22.04 x64 1. 下载源码 1.1 安装码云repo工具 sudo apt install python3-pip git-lfsmkdir ~/bin curl https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 -o ~/bin/repo chmod ax ~/bin/repo pip3 install -i https://repo.huaweicloud.com/repository/p…...
 
PostgreSQL 数据库使用 psql 导入 SQL
最近我们有一个 SQL 需要导入到 PostgreSQL ,但数据格式使用的是用: -- -- TOC entry 7877 (class 0 OID 21961) -- Dependencies: 904 -- Data for Name: upload_references; Type: TABLE DATA; Schema: public; Owner: - --COPY public.upload_refere…...
 
容器编排学习(三)端口映射与Harber镜像仓库介绍
一 对外发布服务(端口映射) 1 概述 新创建容器的IP 地址是随机的 容器在重启后每次 IP 都会发生变化 容器服务只有宿主机才能访问 如何才能使用容器对外提供稳定的服务? 容器端口可以与宿主机的端口进行映射绑定 从而把宿主机变成对应的服务&a…...
 
Day_13 > 指针进阶(2)
目录 1.函数指针数组 2.指向函数指针数组的指针 3.回调函数 qsort()函数 代码示例 void* 4.结束 今天我们在进阶指针的基础上,学习进阶指针的第二部分 1.函数指针数组 首先我们回顾一下指针数组 char* arr[5]://字符指针数组 - 数组 - 存放的是字符指针 in…...
 
对Transformer中的Attention(注意力机制)的一点点探索
摘要:本文试图对 Transformer 中的 Attention 机制进行一点点探索。并就 6 个问题深入展开。 ✅ NLP 研 1 选手的学习笔记 简介:小王,NPU,2023级,计算机技术 研究方向:文本生成、摘要生成 文章目录 一、为啥…...
车内信息安全技术-安全技术栈-软件安全
操作系统 1.隔离技术 信息安全中的隔离技术通常指的是将不同安全级别的信息或数据隔离开来,以保护敏感信息不受未授权的访问或泄露。在操作系统中,常见的隔离技术包括:虚拟化技术:通过虚拟化软件,将物理计算机分割成多个独立的虚拟计算机,每个虚拟计算机都可以运行独立的…...
 
Redis常见命令
命令可以查看的文档 http://doc.redisfans.com/ https://redis.io/commands/ 官方文档(英文) http://www.redis.cn/commands.html 中文 https://redis.com.cn/commands.html 个人推荐这个 https://try.redis.io/ redis命令在线测试工具 https://githubfa…...
 
Android Studio实现一笔画完小游戏
文章目录 一、项目概述二、开发环境三、详细设计3.1、数据库设计3.2、普通模式3.3、随机模式3.4、关卡列表 四、运行演示五、项目总结六、源码获取 一、项目概述 Android一笔画完是一种益智游戏,玩家需要从起点开始通过一条连续的线,将图形中所有的方块…...
【Python 程序设计】数据人员入门【02/8】
一、说明 介绍如何管理 Python 依赖项和一些虚拟环境最佳实践。 以下文章是有关 Python 数据工程系列文章的一部分,旨在帮助数据工程师、数据科学家、数据分析师、机器学习工程师或其他刚接触 Python 的人掌握基础知识。迄今为止,本初学者指南包括&#…...
 
学习笔记——树上哈希
普通子树哈希 树上的很多东西都是转化成链上问题的,比如树上哈希 树上哈希,主要是用于树的同构这个东西上的 什么是树的同构? 如图,不考虑节点编号,三棵树是同构的 将树转化成链,一般有两种方式…...
 
Opencv快速入门教程,Python计算机视觉基础
快速入门 OpenCV 是 Intel 开源计算机视觉库。它由一系列 C 函数和少量 C 类构成, 实现了图像处理和计算机视觉方面的很多通用算法。 OpenCV 拥有包括 300 多个 C 函数的跨平台的中、高层 API。它不依赖于其它的外部库——尽管也 可以使用某些外部库。 OpenCV 对非…...
laravel 报错误信息 Carbon\Exceptions\InvalidFormatException
Carbon\Exceptions\InvalidFormatException Unexpected data found. at vendor\nesbot\carbon\src\Carbon\Traits\Creator.php:687 683▕ return $instance; 684▕ } 685▕ 686▕ if (static::isStrictModeEnabled()) { ➜ 687…...
 
UI自动化之混合框架
什么是混合框架,混合框架就是将数据驱动与关键字驱动结合在一起,主要用来回归业务主流程,将核心流程串联起来。 上一篇我们写到了关键字驱动框架,关键字驱动框架是针对一个业务场景的单条测试用例的。 我们以163邮箱的登录到创建…...
SQL创建用户-非DM8.2环境(达梦数据库)
DM8:达梦数据库SQL创建用户-非DM8.2环境 环境介绍 环境介绍 在没有图形化界面,或者想快速创建用户,可以使用一下SQL语句;将其中的 CESHI 替换为要创建的用户名即可,默认创建了数据表空间,索引表空间,文件大…...
Thread类中run和start的区别
答:调用线程类中的 start 方法,才开始创建并启动线程,而线程被回收,则是要执行完线程的入口方法(对于主线程来说,则是要执行完 main 方法),这里要回收线程则是要将(&…...
ElementUI浅尝辄止35:Checkbox 多选框
一组备选项中进行多选 1.如何使用? 单独使用可以表示两种状态之间的切换,写在标签中的内容为 checkbox 按钮后的介绍。 //在el-checkbox元素中定义v-model绑定变量,单一的checkbox中,默认绑定变量的值会是Boolean,选…...
 
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
 
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
 
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
 
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
 
论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving
地址:LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂,正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...
 
【大模型】RankRAG:基于大模型的上下文排序与检索增强生成的统一框架
文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构C.1 指令微调阶段C.2 排名与生成的总和指令微调阶段C.3 RankRAG推理:检索-重排-生成 D 实验设计E 个人总结 A 论文出处 论文题目:RankRAG:Unifying Context Ranking…...
 
高保真组件库:开关
一:制作关状态 拖入一个矩形作为关闭的底色:44 x 22,填充灰色CCCCCC,圆角23,边框宽度0,文本为”关“,右对齐,边距2,2,6,2,文本颜色白色FFFFFF。 拖拽一个椭圆,尺寸18 x 18,边框为0。3. 全选转为动态面板状态1命名为”关“。 二:制作开状态 复制关状态并命名为”开…...
基于 HTTP 的单向流式通信协议SSE详解
SSE(Server-Sent Events)详解 🧠 什么是 SSE? SSE(Server-Sent Events) 是 HTML5 标准中定义的一种通信机制,它允许服务器主动将事件推送给客户端(浏览器)。与传统的 H…...
 
react-pdf(pdfjs-dist)如何兼容老浏览器(chrome 49)
之前都是使用react-pdf来渲染pdf文件,这次有个需求是要兼容xp环境,xp上chrome最高支持到49,虽然说iframe或者embed都可以实现预览pdf,但为了后续的定制化需求,还是需要使用js库来渲染。 chrome 49测试环境 能用的测试…...
