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,选…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
