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

news2024/11/22 9:22:12

文章目录

  • 组件通信
    • 组件通信的意义
    • 父传子实现
    • 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>
        <Son
          msg={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 } = props
  return (
    <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 =>
            <ListItem
              key={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.colors
  const 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和props
  constructor(props) {
    // 调用super方法,传入props
    super(props);
    // 初始化state,设置count为0
    this.state = {
      count: 0,
    };
  }

  // 定义一个生命周期函数,用来在组件挂载时执行一些操作
  // 使用箭头函数,不需要使用bind方法或者闭包来绑定this
  componentDidMount = () => {
    // 在控制台输出一条信息,表示组件已经挂载
    console.log("The component is mounted.");
  };

  // 定义一个生命周期函数,用来在组件更新时执行一些操作
  // 使用箭头函数,不需要使用bind方法或者闭包来绑定this
  componentDidUpdate = () => {
    // 在控制台输出一条信息,表示组件已经更新,以及当前的count值
    console.log("The component is updated. The count is " + this.state.count);
  };

  // 定义一个生命周期函数,用来在组件卸载时执行一些操作
  // 使用箭头函数,不需要使用bind方法或者闭包来绑定this
  componentWillUnmount = () => {
    // 在控制台输出一条信息,表示组件已经卸载
    console.log("The component is unmounted.");
  };

  // 定义一个方法,用来增加count的值
  // 使用箭头函数,不需要使用bind方法或者闭包来绑定this
  increment = () => {
    // 使用setState方法,将count的值加一
    this.setState((prevState) => ({
      count: prevState.count + 1,
    }));
  };

  // 定义一个方法,用来减少count的值
  // 使用箭头函数,不需要使用bind方法或者闭包来绑定this
  decrement = () => {
    // 使用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.coloradmin.cn/o/1185395.html

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

相关文章

0成本LLM微调上手项目,⚡️一步一步使用colab训练法律LLM,基于microsoft/phi-1_5,包含lora微调,全参微调

项目地址 &#xff1a;https://github.com/billvsme/train_law_llm ✏️LLM微调上手项目 一步一步使用Colab训练法律LLM&#xff0c;基于microsoft/phi-1_5 。通过本项目你可以0成本手动了解微调LLM。 nameColabDatasets自我认知lora-SFT微调train_self_cognition.ipynbsel…

P1131 [ZJOI2007] 时态同步

Portal. 先找出树上以 S S S 为起点最长的一条链&#xff0c;然后让其他链的长度都和该链对齐即可。 维护每个结点 x x x 的子树最长链 d max ⁡ ( x ) d_{\max}(x) dmax​(x)&#xff0c;则每次 DFS 求出最长链之后调整对齐的代价为 d max ⁡ ( x ) − ( d max ⁡ ( s o …

Java算法(二):数组元素求和(元素个位和十位不能是 7 ,且只能是偶数)

java算法&#xff08;二&#xff09; 需求&#xff1a; ​ 有这样一个数组&#xff1a; 元素是&#xff1a;{68, 27, 95, 88, 171, 996, 51, 210} ​ 求出该数组中满足要求的元素和 ​ 要求是&#xff1a; 求和的元素各位和十位都不能是 7 &#xff0c;并且只能是偶数 packa…

机器学习中的假设检验

正态性检验相关分析回归分析 所谓假设检验&#xff0c;其实就是根据原假设来构造一种已知分布的统计量来计算概率&#xff0c;根据概率值大小来判断能否拒绝原假设&#xff0c;从而得到一种结论。假设检验的过程就是&#xff0c;构造一个原假设成立条件下的事件A&#xff0c;计…

如何实现单病种上报的多院区/集团化/平台联动管理

背 景 米软售前人员在了解客户单病种上报的相关需求中发现&#xff0c;部分医院分为本部、分部或总院、分院等多个院区&#xff0c;各院区需共用一套系统&#xff1b;部分医院与其他兄弟医院隶属于同一集团医院&#xff0c;全集团需统一部署&#xff1b;部分市/区卫健委要求全…

【Node.js入门】1.3 开始开发Node.js应用程序

1.3 开始开发Node.js应用程序 学习目标 &#xff08;1&#xff09;熟悉开发工具Visual Studio Code的基本使用&#xff1b; &#xff08;2&#xff09;掌握Node.js应用程序的编写、运行和调试的基本方法。 构建第一个 Node.js应用程序 代码 const http require("htt…

RabbitMQ 消息中间件 消息队列

RabbitMQ1、RabbitMQ简介2、RabbitMQ 特点3、什么是消息队列4、RabbiMQ模式5、集群中的基本概念 单实例安装RabbitMQ安装依赖安装erlang安装rabbitmq开启rabbitmq的web访问界面添加用户修改配置文件重启服务浏览器访问Rabbit-test rabbitMQ集群准备工作&#xff08;三台&#x…

AM@向量代数@向量基本概念和向量线性运算

文章目录 abstract向量的基本概念向量向量的坐标分解式和坐标&#x1f47a;向量的模向量的长度(大小)&#x1f47a;零向量单位向量&#x1f47a;方向向量非零向量的单位向量正规化向量夹角&#x1f47a; 向量方向角和向量间夹角投影几何描述向量的线性运算向量的加减运算向量的…

【STM32 开发】| INA219采集电压、电流值

目录 前言1 原理图2 IIC地址说明3 寄存器地址说明4 开始工作前配置5 程序代码1&#xff09;驱动程序2&#xff09;头文件3) 测试代码 前言 INA219 是一款具备 I2C 或 SMBUS 兼容接口的分流器和功率监测计。该器件监测分流器电压降和总线电源电压&#xff0c;转换次数和滤波选项…

jmeter+ant实现的接口自动化测试

jmeterANT接口自动化测试框架 项目说明 本框架是一套基于jmeterAntExcelPython而设计的数据驱动接口自动化测试框架&#xff0c;jmeter 作为执行器&#xff0c;Ant 作为构建工具&#xff0c;进行构建测试&#xff0c;本框架无需你使用代码编写用例&#xff0c;测试用例存储在…

基于CSP的运动想象EEG分类任务实战

基于运动想象的公开数据集&#xff1a;Data set IVa (BCI Competition III)1 数据描述参考前文&#xff1a;https://blog.csdn.net/qq_43811536/article/details/134224005?spm1001.2014.3001.5501 EEG 信号时频空域分析参考前文&#xff1a;https://blog.csdn.net/qq_4381153…

算法竞赛——数论(一),数论内容的介绍,基础数论

文章目录 一&#xff0c; 数论学习路线的介绍和相关建议1&#xff0c;建议学习人群 &#xff1a;2&#xff0c;建议学习时长3&#xff0c;学习路线的介绍1&#xff0c;基础数论2&#xff0c;组合数学3&#xff0c;计算几何 二&#xff0c;基础数论第一部分 —— 快速幂和快速幂…

2023年十大最佳 iPhone 恢复软件

您的 iPhone 存储了大量有价值的数据&#xff0c;包括照片和视频。但是&#xff0c;即使使用高度安全的 Apple 设备&#xff0c;数据丢失也可能随时发生。从众多可用工具中选择最适合 iPhone 的数据恢复软件可能是一项艰巨的任务。因此&#xff0c;我们测试了性能并审查了 2023…

使用Hypothesis生成测试数据

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

[vue-router]vue3.x Hash路由前缀问题

[vue-router]vue3.x Hash路由前缀问题 问题描述问题分析 问题描述 是在本地开发时&#xff0c;使用的HASH路由&#xff0c;然后在偶然的情况下在/#/前添加了前缀&#xff0c;发现不影响本地的路由的使用&#xff1f;&#xff1f;&#xff1f;&#xff01;&#xff01;&#xf…

rviz中引入SW的模型

一、SW装配图转urdf 参考链接&#xff1a;https://blog.csdn.net/weixin_45168199/article/details/105755388 这部分直接看参考链接就可以&#xff0c;主要思路如下 1、把sw中的零散零件按照机器人中连杆的分类整合成几个大零件 2、把几个大零件整合成装配体&#xff0c;并…

如何在Jetpack Compose中显示PDF?

当读取和显示 PDF 的组件缺失时该怎么办? 声明式编程可以拯救你. Jetpack Compose已经存在好几年了, 但_在某些方面它的使用仍然面临挑战_. 例如, 缺少用于查看PDF的官方组件, 而为数不多的第三方库通常也是有代价的. 在我们的应用中, 我们会遇到在许多场景中显示 PDF 的需求…

跨境电商源码独立开发:一次购买,终生使用

随着全球电子商务的快速发展&#xff0c;越来越多的企业开始涉足跨境电商领域。为了在这个竞争激烈的市场中脱颖而出&#xff0c;您需要一个专业的跨境电商解决方案。我们的团队为您提供最优质的源码独立开发服务&#xff0c;让您拥有一个功能强大、安全稳定的跨境电商平台。 一…

腾讯觅影数智医疗影像平台获颁世界互联网领先科技成果大奖

11月8日&#xff0c;2023年世界互联网大会乌镇峰会在乌镇举行&#xff0c;腾讯再度获颁“世界互联网领先科技成果”大奖。腾讯健康总裁吴文达在世界互联网领先科技成果发布活动中介绍&#xff0c;“腾讯觅影数智医疗影像平台”已全面开放20多个医疗AI引擎助力科研创新&#xff…

2007-2022年全国各地级市金融机构网点数据

2007-2022年地级市金融机构网点数据 1、时间&#xff1a;2007-2022年 2、指标&#xff1a;行政区划代码、年份、城市名称、所属省份、银行网点数量、其中-政策性银行及国家开发银行营业网点占比、其中-商业银行营业网点数量占比、其中-农村金融机构营业网点数量占比 3、范围…