React进阶之路(二)-- 组件通信、组件进阶

文章目录

  • 组件通信
    • 组件通信的意义
    • 父传子实现
    • props说明
    • 子传父实现
    • 兄弟组件通信
    • 跨组件通信Context
    • 通信案例
  • React组件进阶
    • children属性
    • props校验
    • 组件生命周期

组件通信

组件通信的意义

组件是独立且封闭的单元,默认情况下组件只能使用自己的数据(state)
组件化开发的过程中,完整的功能会拆分多个组件,在这个过程中不可避免的需要互相传递一些数据
为了能让各组件之间可以进行互相沟通,数据传递,这个过程就是组件通信

  1. 父子关系 - 最重要的
  2. 兄弟关系 - 自定义事件模式产生技术方法 eventBus / 通过共同的父组件通信
  3. 其它关系 - mobx / redux / zustand

父传子实现

实现步骤

  1. 父组件提供要传递的数据 - state (可以不满足,例如传递方法的时候)
  2. 给子组件标签添加属性,值为 state中的数据
  3. 子组件中通过 props 接收父组件中传过来的数据
    • 类组件使用this.props获取props对象
    • 函数式组件直接通过参数获取props对象

在子组件标签上添加的属性,都会成为该子组件props属性(对象)上的值

接下来我们来实现一下如下的场景:

在这里插入图片描述
实现代码:

import React from 'react'// 函数式子组件
function FSon(props) {console.log(props)return (<div>子组件1{props.msg}</div>)
}// 类子组件
class CSon extends React.Component {render() {return (<div>子组件2{this.props.msg}</div>)}
}
// 父组件
class App extends React.Component {state = {message: 'this is message'}render() {return (<div><div>父组件</div><FSon msg={this.state.message} /><CSon msg={this.state.message} /></div>)}
}export default App

props说明

  1. props是只读对象(readonly)
    根据单项数据流的要求,子组件只能读取props中的数据,不能进行修改

  2. props可以传递任意数据
    数字、字符串、布尔值、数组、对象、函数JSX

class App extends React.Component {state = {message: 'this is message'}render() {return (<div><div>父组件</div><FSon msg={this.state.message} age={20} isMan={true} cb={() => { console.log(1) }} child={<span>this is child</span>}/><CSon msg={this.state.message} /></div>)}
}

在这里插入图片描述
同时我们在使用props的时候通常会使用解构赋值,例如:

在这里插入图片描述

当然我们也可以直接在函数的参数位置进行解构赋值:

在这里插入图片描述

子传父实现

本质: 父组件给子组件传递回调函数,子组件调用

实现步骤

  1. 父组件提供一个回调函数 - 用于接收数据
  2. 将函数作为属性的值,传给子组件
  3. 子组件通过props调用 回调函数
  4. 将子组件中的数据作为参数传递给回调函数

接下来我们来实现一下如下的场景:
在这里插入图片描述

代码实现:

import React from 'react'// 子组件
function Son(props) {function handleClick() {// 调用父组件传递过来的回调函数 并注入参数props.changeMsg('this is newMessage')}return (<div>{props.msg}<button onClick={handleClick}>change</button></div>)
}class App extends React.Component {state = {message: 'this is message'}// 提供回调函数changeMessage = (newMsg) => {console.log('子组件传过来的数据:',newMsg)this.setState({message: newMsg})}render() {return (<div><div>父组件</div><Sonmsg={this.state.message}// 传递给子组件changeMsg={this.changeMessage}/></div>)}
}export default App

兄弟组件通信

核心思路: 通过状态提升机制,利用共同的父组件实现兄弟通信。也就是说通过子传父+父传子来实现。

在这里插入图片描述

实现步骤

  1. 将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态
    • 提供共享状态
    • 提供操作共享状态的方法
  2. 要接收数据状态的子组件通过 props 接收数据
  3. 要传递数据状态的子组件通过props接收方法,调用方法传递数据

代码实现:

import React from 'react'// 子组件A
function SonA(props) {return (<div>SonA{props.msg}</div>)
}
// 子组件B
function SonB(props) {return (<div>SonB<button onClick={() => props.changeMsg('new message')}>changeMsg</button></div>)
}// 父组件
class App extends React.Component {// 父组件提供状态数据state = {message: 'this is message'}// 父组件提供修改数据的方法changeMsg = (newMsg) => {this.setState({message: newMsg})}render() {return (<>{/* 接收数据的组件 */}<SonA msg={this.state.message} />{/* 修改数据的组件 */}<SonB changeMsg={this.changeMsg} /></>)}
}export default App

跨组件通信Context

在这里插入图片描述
上图是一个react形成的嵌套组件树,如果我们想从App组件向任意一个下层组件传递数据,该怎么办呢?目前我们能采取的方式就是一层一层的props往下传,显然很繁琐

那么,Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法

实现步骤

  1. 创建Context对象 导出 Provider 和 Consumer对象

    const { Provider, Consumer } = createContext()
    
  2. 使用Provider包裹上层组件提供数据 ,注意:Provider组件不一定必须包裹根组件,但是必须包裹需要共享数据的子组件的最近公共祖先组件。这样,Provider组件可以将数据通过Context对象传递给所有的子孙组件,而不需要通过props逐层传递

    <Provider value={this.state.message}>{/* 根组件 */}
    </Provider>
    
  3. 需要用到数据的组件使用Consumer包裹获取数据 ,注意:这里括号里面是一个箭头函数的方法声明,形式不能改变!!!

    <Consumer >{value => /* 基于 context 值进行渲染*/}
    </Consumer>
    

代码实现:

import React, { createContext }  from 'react'// 1. 创建Context对象 
const { Provider, Consumer } = createContext()// 3. 消费数据
function ComC() {return (<Consumer >{value => <div>{value}</div>}</Consumer>)
}function ComA() {return (<ComC/>)
}// 2. 提供数据
class App extends React.Component {state = {message: 'this is message'}render() {return (<Provider value={this.state.message}><div className="app"><ComA /></div></Provider>)}
}export default App

通信案例

要求:App为父组件用来提供列表数据 ,ListItem为子组件用来渲染列表数据

在这里插入图片描述

// 列表数据
[{ id: 1, name: '超级好吃的棒棒糖', price: 18.8, info: '开业大酬宾,全场8折' },{ id: 2, name: '超级好吃的大鸡腿', price: 34.2, info: '开业大酬宾,全场8折' },{ id: 3, name: '超级无敌的冰激凌', price: 14.2, info: '开业大酬宾,全场8折' }
]

代码实现:

import React from 'react'// 子组件
function ListItem(props) {const { name, price, info, id, delHandler } = propsreturn (<div><h3>{name}</h3><p>{price}</p><p>{info}</p><button onClick={() => delHandler(id)}>删除</button></div>)
}// 父组件
class App extends React.Component {state = {list: [{ id: 1, name: '超级好吃的棒棒糖', price: 18.8, info: '开业大酬宾,全场8折' },{ id: 2, name: '超级好吃的大鸡腿', price: 34.2, info: '开业大酬宾,全场8折' },{ id: 3, name: '超级无敌的冰激凌', price: 14.2, info: '开业大酬宾,全场8折' }]}delHandler = (id) => {this.setState({list: this.state.list.filter(item => item.id !== id)})}render() {return (<>{this.state.list.map(item =><ListItemkey={item.id}{...item}delHandler={this.delHandler} />)}</>)}
}export default App

React组件进阶

children属性

children属性表示该组件的子节点,只要组件内部有子节点,props中就有该属性

children可以是:

  1. 普通文本
  2. 普通标签元素
  3. 函数 / 对象
  4. JSX

props校验

在ts中这个问题得到了很好的解决,不需要第三方依赖的支持。

对于组件来说,props是由外部传入的,我们其实无法保证组件使用者传入了什么格式的数据,如果传入的数据格式不对,就有可能会导致组件内部错误,有一个点很关键 - 组件的使用者可能报错了也不知道为什么,看下面的例子:
在这里插入图片描述

面对这样的问题,如何解决? props校验

实现步骤

  1. 安装属性校验包:yarn add prop-types
  2. 导入prop-types
  3. 使用 组件名.propTypes = {} 给组件添加校验规则

核心代码:

import PropTypes from 'prop-types'const List = props => {const arr = props.colorsconst lis = arr.map((item, index) => <li key={index}>{item.name}</li>)return <ul>{lis}</ul>
}List.propTypes = {colors: PropTypes.array
}

规则说明

四种常见结构

  1. 常见类型:array、bool、func、number、object、string
  2. React元素类型:element
  3. 必填项:isRequired
  4. 特定的结构对象:shape({})
// 常见类型
optionalFunc: PropTypes.func,
// 必填 只需要在类型后面串联一个isRequired
requiredFunc: PropTypes.func.isRequired,
// 特定结构的对象
optionalObjectWithShape: PropTypes.shape({color: PropTypes.string,fontSize: PropTypes.number
})

官网文档更多阅读:https://reactjs.org/docs/typechecking-with-proptypes.html

如何给组件的props提供默认值

对于函数组件来说

直接使用函数参数默认值

function List({pageSize = 10}) {return (<div>此处展示props的默认值:{ pageSize }</div>)
}// 不传入pageSize属性
<List />

对于类组件来说

使用类静态属性声明默认值,static defaultProps = {}

class List extends Component {static defaultProps = {pageSize: 10}render() {return (<div>此处展示props的默认值:{this.props.pageSize}</div>)}
}
<List />

通过我们上面使用的第三包工具包里的 defaultProps 也可以给组件的props设置默认值,在未传入props的时候生效

组件生命周期

组件的生命周期是指组件从被创建到挂载到页面中运行起来,再到组件不用时卸载的过程,注意,只有类组件才有生命周期(类组件 实例化 函数组件 不需要实例化)

那么我们自然就有一个疑问,既然函数组件没有生命周期,那么他怎么实现类似钩子函数的功能?

  • React函数组件本身是没有生命周期函数的,因为它们只是接收props并返回JSX的纯函数,没有自己的状态和实例。
  • React函数组件可以通过使用Effect Hook来模拟一些生命周期的功能,例如在组件挂载、更新或卸载时执行一些副作用操作¹。
  • React函数组件还可以通过使用其他的Hook来实现一些类组件的特性,例如useState Hook可以让函数组件拥有自己的状态,useRef Hook可以让函数组件访问DOM元素或保存一些可变的值,useCallback Hook和useMemo Hook可以让函数组件优化性能等。

在这里插入图片描述
如何理解render阶段纯净且不包含副作用?

  • React的render阶段是指React组件根据props和state生成虚拟DOM的过程。在这个过程中,React会调用组件的render方法或函数组件本身,返回一个React元素或null
  • React的render阶段是纯净的,也就是说,它不会对组件的props和state进行任何修改,也不会对外部环境产生任何影响。这样可以保证render阶段的可预测性和可测试性,以及避免不必要的渲染和性能损耗。
  • React的render阶段是不包含副作用的,也就是说,它不会对真实的DOM进行任何操作,也不会触发任何生命周期方法或钩子函数。这样可以保证render阶段的纯粹性和高效性,以及避免不必要的副作用和错误。
    • 例如:发送一个请求是一种副作用操作,因为它会对外部环境产生影响,例如改变服务器的状态或者获取数据。因此,发送一个请求不应该在render阶段进行,而应该在其他阶段进行,例如在生命周期方法或钩子函数中。
  • React的render阶段只负责生成虚拟DOM,而不负责渲染到真实的DOM上。这一步由React的commit阶段完成,它会根据虚拟DOM和真实DOM的差异,进行最小化的更新。在commit阶段,React会执行一些副作用操作,例如调用生命周期方法或钩子函数,插入或删除DOM节点,添加或移除事件监听器等。

接下来我们来看一下类组件的三个重要的生命阶段:

  • 挂载
  • 更新
  • 卸载

挂载阶段

在这里插入图片描述
注意:

  • constructer现在用的不是很多了,例如初始化state直接写就行,不用写在constructer里面。
  • 在render里面不要使用setState,因为setState会导致重新渲染从而再次执行render函数,相当于进入了无限循环。

例如:
在这里插入图片描述
在这里插入图片描述

更新阶段

也就是每次更新都会执行的钩子函数
在这里插入图片描述
注意:

  • componentDidUpdate不能直接调用setState,因为setState会造成更新,从而再次调用render函数,这样就会造成无限循环。

卸载阶段

在这里插入图片描述

在这里插入图片描述

注意:react类组件中的生命周期函数可以使用箭头函数表示,例如:

// 导入React和Component
import React, { Component } from "react";// 定义一个计数器组件,继承自Component
class Counter extends Component {// 定义一个构造函数,用来初始化state和propsconstructor(props) {// 调用super方法,传入propssuper(props);// 初始化state,设置count为0this.state = {count: 0,};}// 定义一个生命周期函数,用来在组件挂载时执行一些操作// 使用箭头函数,不需要使用bind方法或者闭包来绑定thiscomponentDidMount = () => {// 在控制台输出一条信息,表示组件已经挂载console.log("The component is mounted.");};// 定义一个生命周期函数,用来在组件更新时执行一些操作// 使用箭头函数,不需要使用bind方法或者闭包来绑定thiscomponentDidUpdate = () => {// 在控制台输出一条信息,表示组件已经更新,以及当前的count值console.log("The component is updated. The count is " + this.state.count);};// 定义一个生命周期函数,用来在组件卸载时执行一些操作// 使用箭头函数,不需要使用bind方法或者闭包来绑定thiscomponentWillUnmount = () => {// 在控制台输出一条信息,表示组件已经卸载console.log("The component is unmounted.");};// 定义一个方法,用来增加count的值// 使用箭头函数,不需要使用bind方法或者闭包来绑定thisincrement = () => {// 使用setState方法,将count的值加一this.setState((prevState) => ({count: prevState.count + 1,}));};// 定义一个方法,用来减少count的值// 使用箭头函数,不需要使用bind方法或者闭包来绑定thisdecrement = () => {// 使用setState方法,将count的值减一this.setState((prevState) => ({count: prevState.count - 1,}));};// 定义一个渲染方法,用来返回组件的视图render() {// 返回一个div元素,包含一个h1元素和一个button元素return (<div><h1>Counter: {this.state.count}</h1><button onClick={this.increment}>+</button><button onClick={this.decrement}>-</button></div>);}
}// 导出Counter组件
export default Counter;

还要注意:生命周期函数是同步的,只有在执行完了之后才会进入下一个生命周期执行下一个生命周期钩子。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/138576.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++破局】C++内存管理之new与deleted剖析

​作者主页 &#x1f4da;lovewold少个r博客主页 ⚠️本文重点&#xff1a;c内存管理部分知识点梳理 &#x1f449;【C-C入门系列专栏】&#xff1a;博客文章专栏传送门 &#x1f604;每日一言&#xff1a;花有重开日&#xff0c;人无再少年&#xff01; 目录 C/C的内存分配机…

Git 命令详解

系列文章目录 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 C技能系列 期待你的关注哦&#xff01;&#xff01;&#xff01; 现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everything is for the future of dream we…

【Python】Python爬虫使用代理IP的实现

前言 在爬虫的过程中&#xff0c;我们经常会遇到需要使用代理IP的情况。比如&#xff0c;针对目标网站的反爬机制&#xff0c;需要通过使用代理IP来规避风险。因此&#xff0c;本文主要介绍如何在Python爬虫中使用代理IP。 一、代理IP的作用 代理IP&#xff0c;顾名思义&…

JVM虚拟机:垃圾回收器之Parallel Old(老年代)

本文重点 本文将学习老年代的另外一种垃圾回收器Parallel Old(PO)&#xff0c;这是一种用于老年代的并行化垃圾回收器&#xff0c;它使用标记整理算法进行垃圾回收。 历史 在1.6之前&#xff0c;新生代使用Parallel Scavenge只能搭配老年代的Serial Old收集器&#xff0c;而…

Ubuntu22.04 下 NFS 相关问题与完整配置(客户机 MacOS)

categories: [Linux-Shell] tags: Linux NFS 写在前面 最近折腾一下 NFS, 先白嫖一顿华子云的 1 个月服务器, 2C4G 感觉不错了, 但NFS 配置起来还是有点难度, 主要还是随机分配的端口配置方面比较恶心. server环境: 华为云 2C4G Ubuntu22.04 client环境: MacOS M1 with brew …

小程序多文件上传 Tdesign

众所周知&#xff0c;小程序文件上传还是有点麻烦的&#xff0c;其实主要还是小程序对的接口有诸多的不便&#xff0c;比如说&#xff0c;文件不能批量提交&#xff0c;只能一个个的提交&#xff0c;小程序的上传需要专门的接口。 普通的小程序的页面也比普通的HTML复杂很多 现…

Java,多线程,线程安全的懒汉式、死锁、ReentrantLock的使用以及一些知识点补充

关于线程安全地懒汉式有以下几种方式&#xff1a; /*** 实现线程安全的懒汉式*/ public class BankTest {Bank b1 null;Bank b2 null;public static void main(String[] args){BankTest bb new BankTest();Thread t1 new Thread(){Overridepublic void run(){bb.b1 Bank.…

安卓RadioButton设置图片大小

RadioButton都不陌生&#xff0c;一般我们都会设置图片在里面&#xff0c;这就涉及一个问题&#xff0c;图片的大小。如果图片过大&#xff0c;效果很不理想。搜了很多方法&#xff0c;都不理想。无奈只能自己研究了 代码如下&#xff1a; 1&#xff0c;一个简单的 RadioButt…

IBM Qiskit量子机器学习速成(一)

声明&#xff1a;本篇笔记基于IBM Qiskit量子机器学习教程的第一节&#xff0c;中文版译文详见&#xff1a;https://blog.csdn.net/qq_33943772/article/details/129860346?spm1001.2014.3001.5501 概述 首先导入关键的包 from qiskit import QuantumCircuit from qiskit.u…

力扣138:随机链表的复制

力扣138&#xff1a;随机链表的复制 题目描述&#xff1a; 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff…

《网络协议》02. 物理层 · 数据链路层 · 网络层

title: 《网络协议》02. 物理层 数据链路层 网络层 date: 2022-08-31 22:26:48 updated: 2023-11-08 06:58:52 categories: 学习记录&#xff1a;网络协议 excerpt: 物理层&#xff08;数据通信模型&#xff0c;信道&#xff09;、数据链路层&#xff08;封装成帧&#xff0c…

NtripShare Mos地铁自动化监测终端盒子硬件设计

自动化监测产品到目前为止做了接近一年&#xff0c;在软件层面上&#xff0c;控制终端软件、平台软件、网平差算法都已解决&#xff0c;硬件盒子始终是心里过不去的坎&#xff0c;最终还是没有耐住性子自己做了一把。 选型如下&#xff1a; 1、主板:瑞芯微RK3568主板。 2、外…

向量的点积和外积

参考&#xff1a;https://www.cnblogs.com/gxcdream/p/7597865.html 一、向量的内积&#xff08;点乘&#xff09; 定义&#xff1a; 两个向量a与b的内积为 ab |a||b|cos∠(a, b)&#xff0c;特别地&#xff0c;0a a0 0&#xff1b;若a&#xff0c;b是非零向量&#xff0c;…

【性能测试】服务端中间件docker常用命令解析整理(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、搜索 docker …

11-08 周三 图解机器学习之实现逻辑异或,理解输出层误差和隐藏层误差项和动量因子

11-08 周三 图解机器学习之实现逻辑异或&#xff0c;理解输出层误差和隐藏层误差项 时间版本修改人描述2023年11月8日14:36:36V0.1宋全恒新建文档 简介 最近笔者完成了《图解机器学习》这本书的阅读&#xff0c;由于最近深度学习网络大行其是&#xff0c;所以也想要好好的弄清…

Effective C++ 系列和 C++ Core Guidelines 如何选择?

Effective C 系列和 C Core Guidelines 如何选择&#xff1f; 如果一定要二选一&#xff0c;我会选择C Core Guidelines。因为它是开源的&#xff0c;有300多个贡献者&#xff0c;而且还在不断更新&#xff0c;意味着它归纳总结了最新的C实践经验。最近很多小伙伴找我&#xff…

基于springboot实现智慧外贸平台系统【项目源码+论文说明】计算机毕业设计

基于springboot实现智慧外贸平台系统演示 摘要 网络的广泛应用给生活带来了十分的便利。所以把智慧外贸管理与现在网络相结合&#xff0c;利用java技术建设智慧外贸平台&#xff0c;实现智慧外贸的信息化。则对于进一步提高智慧外贸管理发展&#xff0c;丰富智慧外贸管理经验能…

Java进阶篇--Executors类创建常见线程池

目录 线程池架构 newSingleThreadExecutor newFixedThreadPool newCachedThreadPool newScheduledThreadPool Executors和ThreaPoolExecutor创建线程池的区别 两种提交任务的方法 线程池架构 线程池是一种线程管理的机制&#xff0c;用于维护和复用线程&#xff0c;以…

Leetcode2834. 找出美丽数组的最小和

Every day a Leetcode 题目来源&#xff1a;2834. 找出美丽数组的最小和 解法1&#xff1a;贪心 从最小正整数 1 开始枚举&#xff0c;设当前数为 num&#xff0c;如果 nums 里没有 target - num&#xff0c;就说明可以添加 num&#xff0c;依次填满直到有 n 个数即可。 用…

【k8s-1】基于docker Desktop一键式搭建k8s环境

在docker desktop中一键启动k8s环境很简单。 下面介绍如何启动dashboard&#xff0c;dashboard仪表盘是新手学习k8s至关重要的一个工具。 1、配置控制台 kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.5.1/aio/deploy/recommended.yaml 2、开…