【React系列】非父子组件通信—Context.Provider共享数据、events库事件总线通信

本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg5MDAzNzkwNA==&action=getalbum&album_id=1566025152667107329)

一. Context使用

1.1. Context应用场景

非父子组件数据的共享:

  • 在开发中,比较常见的数据传递方式是通过props属性自上而下(由父到子)进行传递。
  • 但是对于有一些场景:比如一些数据需要在多个组件中进行共享(地区偏好、UI主题、用户登录状态、用户信息等)。
  • 如果我们在顶层的App中定义这些信息,之后一层层传递下去,那么对于一些中间层不需要数据的组件来说,是一种冗余的操作。

我们来看一个例子:

import React, { Component } from 'react';function ProfileHeader(props) {return (<div><h2>用户昵称: {props.nickname}</h2><h2>用户等级: {props.level}</h2></div>)
}class Profile extends Component {render() {return (<div><ProfileHeader nickname={this.props.nickname} level={this.props.level} /><ul><li>设置1</li><li>设置2</li><li>设置3</li><li>设置4</li><li>设置5</li></ul></div>)}
}export default class App extends Component {constructor() {super();this.state = {nickname: "coderwhy",level: 99}}render() {const { nickname, level } = this.state;return (<div><Profile nickname={nickname} level={level} /><h2>其他内容</h2></div>)}
}

我这边顺便补充一个小的知识点:Spread Attributes

  • https://zh-hans.reactjs.org/docs/jsx-in-depth.html

下面两种写法是等价的:

function App1() {return <Greeting firstName="Ben" lastName="Hector" />;
}function App2() {const props = {firstName: 'Ben', lastName: 'Hector'};return <Greeting {...props} />;
}

那么我们上面的Profile的传递代码可以修改为如下代码:

<ProfileHeader {...this.props}/>

但是,如果层级更多的话,一层层传递是非常麻烦,并且代码是非常冗余的:

  • React提供了一个API:Context
  • Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props
  • Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言;

1.2. Context相关的API

React.createContext

const MyContext = React.createContext(defaultValue);

创建一个需要共享的Context对象:

  • 如果一个组件订阅了Context,那么这个组件会从离自身最近的那个匹配的 Provider 中读取到当前的context值;
  • defaultValue是组件在顶层查找过程中没有找到对应的Provider,那么就使用默认值

Context.Provider

<MyContext.Provider value={/* 某个值 */}>...
</MyContext.Provider>

每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化:

  • Provider 接收一个 value 属性,传递给消费组件;
  • 一个 Provider 可以和多个消费组件有对应关系;
    多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据;

Providervalue 值发生变化时,它内部的所有消费组件都会重新渲染;

Class.contextType

class MyClass extends React.Component {componentDidMount() {let value = this.context;/* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */}componentDidUpdate() {let value = this.context;/* ... */}componentWillUnmount() {let value = this.context;/* ... */}render() {let value = this.context;/* 基于 MyContext 组件的值进行渲染 */}
}
MyClass.contextType = MyContext;

挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象:

  • 这能让你使用 this.context 来消费最近 Context 上的那个值;
  • 你可以在任何生命周期中访问到它,包括 render 函数中;

Context.Consumer

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

这里,React 组件也可以订阅到 context 变更。这能让你在 函数式组件 中完成订阅 context

  • 这里需要 函数作为子元素(function as child)这种做法;
  • 这个函数接收当前的 context 值,返回一个 React 节点;

1.3. Context使用过程

我们先按照前面三个步骤来使用一个Context

import React, { Component } from 'react';const UserContext = React.createContext({ nickname: "默认", level: -1 })class ProfileHeader extends Component {render() {return (<div><h2>用户昵称: {this.context.nickname}</h2><h2>用户等级: {this.context.level}</h2></div>)}
}ProfileHeader.contextType = UserContext;class Profile extends Component {render() {return (<div><ProfileHeader /><ul><li>设置1</li><li>设置2</li><li>设置3</li><li>设置4</li><li>设置5</li></ul></div>)}
}export default class App extends Component {render() {return (<div><UserContext.Provider value={{ nickname: "why", level: 99 }}><Profile /></UserContext.Provider><h2>其他内容</h2></div>)}
}
  • 我们就会发现,这个过程中Profile是不需要有任何的数据传递的。

什么时候使用默认值 defaultValue 呢? 如果出现了如下代码:

<Profile />
<UserContext.Provider value={{ nickname: "why", level: 99 }}>
</UserContext.Provider>
  • <Profile />并没有作为 UserContext.Provider 的子组件;

什么时候使用 Context.Consumer 呢?

  1. 当使用value的组件是一个函数式组件时;
  2. 当组件中需要使用多个Context时;

演练一:

function ProfileHeader(props) {return (<div><UserContext.Consumer>{value => {return (<div><h2>用户昵称: {value.nickname}</h2><h2>用户等级: {value.level}</h2></div>)}}</UserContext.Consumer></div>)
}

演练二:当使用value的组件是一个函数式组件时;

  1. 创建一个新的Context
const ThemeContext = React.createContext({ color: "black" });
  1. Provider的嵌套
<UserContext.Provider value={{ nickname: "why", level: 99 }}><ThemeContext.Provider value={{color: "red"}}><Profile /></ThemeContext.Provider>
</UserContext.Provider>
  1. 使用Consumer的嵌套
<UserContext.Consumer>{value => {return (<ThemeContext.Consumer>{theme => (<div><h2 style={theme}>用户昵称: {value.nickname}</h2><h2 style={theme}>用户等级: {value.level}</h2></div>)}</ThemeContext.Consumer>)}}
</UserContext.Consumer>

更多用法可以参考官网:https://zh-hans.reactjs.org/docs/context.html

二. 事件总线

2.1. 事件总线的概述

前面通过Context主要实现的是数据的共享,但是在开发中如果有跨组件之间的事件传递,应该如何操作呢?

  • 在Vue中我们可以通过Vue的实例,快速实现一个事件总线(EventBus),来完成操作;
  • 在React中,我们可以依赖一个使用较多的库 events 来完成对应的操作;

我们可以通过npm或者yarn来安装events

yarn add events;

events常用的API:

  • 创建EventEmitter对象:eventBus对象;
  • 发出事件:eventBus.emit("事件名称", 参数列表);
  • 监听事件:eventBus.addListener("事件名称", 监听函数)
  • 移除事件:eventBus.removeListener("事件名称", 监听函数)

2.2. 案例演练

我们通过一个案例来进行演练:

import React, { Component } from 'react';
import { EventEmitter } from "events";const eventBus = new EventEmitter();class ProfileHeader extends Component {render() {return (<div><button onClick={e => this.btnClick()}>按钮</button></div>)}btnClick() {eventBus.emit("headerClick", "why", 18);}
}class Profile extends Component {render() {return (<div><ProfileHeader /><ul><li>设置1</li><li>设置2</li><li>设置3</li><li>设置4</li><li>设置5</li></ul></div>)}
}export default class App extends Component {componentDidMount() {eventBus.addListener("headerClick", this.headerClick)}headerClick(name, age) {console.log(name, age);}componentWillUnmount() {eventBus.removeListener("headerClick", this.headerClick);}render() {return (<div><Profile/><h2>其他内容</h2></div>)}
}

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

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

相关文章

【SpringCloud】设计原则之数据一致性与设计模式

一、设计原则之数据一致性 数据一致性分以下几种情况。 强一致性 当更新操作完成之后&#xff0c;任何多个后续进程或线程的访问都会返回最新的更新过的值。这种是对用户最友好的&#xff0c;就是用户上一次写什么&#xff0c;下一次就保证能读到什么。根据 CAP 理论&#…

RBAC基于角色的访问控制

一 什么是RBAC 概念 RBAC 是基于角色的访问控制&#xff08;Role-Based Access Control &#xff09;在 RBAC 中&#xff0c;权限与角色相关联&#xff0c;用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的&#…

Python 设置文件资源锁定

功能背景 使用线程操作或者多个程序同时对同一个excel文件进行读写操作&#xff0c;不可避免的会操作冲突的问题&#xff0c;有没有一种方法就是可以不冲突&#xff0c;而是等待上一个程序使用完成后再对文件进行操作&#xff0c;就是等待一个完成再接着下一个&#xff1f; 实现…

SD杂症:TemporalKit的key关键帧无图片问题

本地安装好temporalkit后&#xff0c;进行Ebsynth预处理后&#xff0c;居然发现key文件夹里&#xff0c;没有一张图片 搜遍百度&#xff0c;搜到了原因&#xff0c;也搜到了解决方案 但是。。。。这个解决方案我觉得很不cool&#xff0c;像个打补丁的老师傅&#xff0c;缝缝补补…

深度掌握GitHub Copilot:提高编码效率的终极指南

引言&#xff1a; GitHub Copilot&#xff0c;作为一款由OpenAI和GitHub合作打造的智能代码助手&#xff0c;正在迅速改变开发者的编码体验。本文将深入研究GitHub Copilot&#xff0c;从安装和基础用法到高级应用和实用技巧&#xff0c;助您充分发挥这一工具在提高编码效率和…

Linux第16步_安装NFS服务

NFS&#xff08;Network File System&#xff09;是一种在网络上实现的分布式文件系统&#xff0c;它允许不同的操作系统和设备之间共享文件和资源。 在创建的linux目录下&#xff0c;再创建一个“nfs“文件夹&#xff0c;用来供nfs服务器使用&#xff0c;便于”我们的开发板“…

GeoServe本地部署结合内网穿透实现远程访问Web管理界面

文章目录 前言1.安装GeoServer2. windows 安装 cpolar3. 创建公网访问地址4. 公网访问Geo Servcer服务5. 固定公网HTTP地址 前言 GeoServer是OGC Web服务器规范的J2EE实现&#xff0c;利用GeoServer可以方便地发布地图数据&#xff0c;允许用户对要素数据进行更新、删除、插入…

springboot中使用mongodb进行简单的查询

文章目录 引言依赖配置文件代码编写1、实体类2、使用repository查询3、使用MongoTemplate查询 引言 应用程序中&#xff0c;通常会使用两种数据库&#xff0c;一种是关系型数据库如mysql等&#xff0c;另一种则是非关系型数据库例如mongodb&#xff0c;今天我们就来讲一讲如何…

PPT插件-大珩助手-文字整理功能介绍

删空白行 删除文本中的所有空白行 清理编号 删除文本中的段落编号 清理格式 删除文本中的换行、空格符号 清理艺术 删除文本的艺术字效果 清理边距 删除文本框与文字之间的间隙 软件介绍 PPT大珩助手是一款全新设计的Office PPT插件&#xff0c;它是一款功能强大且实…

【Python案例实战】水质安全分析及建模预测

一、引言 1.水资源的重要性 水是生命之源,是人类生存和发展的基础。它是生态系统中不可或缺的组成部分,对于维系地球上的生命、农业、工业、城市发展等方面都具有至关重要的作用。 2.水质安全与人类健康的关系 水质安全直接关系到人类的健康和生存。水中的污染物和有害物…

苹果Mac电脑PDF优化压缩推荐 Recompress中文 for Mac

Recompress采用了先进的压缩技术&#xff0c;能够智能地分析PDF文件并确定哪些内容可以进一步压缩以节省存储空间。中文界面&#xff1a;Recompress Mac版提供了符合国人浏览习惯的中文界面&#xff0c;使得用户在使用时更加方便。批量压缩&#xff1a;Recompress支持批量压缩P…

Linux三剑客 grep、sed和awk浅析

grep、sed和awk是文本处理中最强大的三个命令行工具。它们在Linux中广泛用于文本搜索、替换、格式化和报告&#xff0c;下面我将详细介绍相关语法和使用案例。 grep (Global Regular Expression Print) grep是一个模式搜索工具&#xff0c;它使用正则表达式来搜索文本&#x…

海外直邮革命:跨境电商的全球化销售策略

在数字化时代&#xff0c;跨境电商正经历着一场革命&#xff0c;而海外直邮成为这场变革的引领者。全球范围内&#xff0c;越来越多的企业通过直邮方式销售商品&#xff0c;打破了地域限制&#xff0c;实现了商品的全球化流通。本文将深入探讨海外直邮的崛起&#xff0c;以及跨…

Spring Cloud Config相关问题及答案(2024)

1、什么是 Spring Cloud Config&#xff0c;它解决了哪些问题&#xff1f; Spring Cloud Config 是一个为微服务架构提供集中化外部配置支持的项目。它是构建在 Spring Cloud 生态系统之上&#xff0c;利用 Spring Boot 的开发便利性&#xff0c;简化了分布式系统中的配置管理…

文件管理方法和技巧,清除文件名中的大写字母

如何将多个文件名称中的大写字母都清除掉&#xff0c;有没有批量操作的方法&#xff1f;这个还真的有&#xff0c;今天小编就给各位朋友介绍一款很好用的软件——文件批量改名高手&#xff0c;助大伙一键清除多个文件名称中的所有大写字母。 所需工具&#xff1a; 一个【文件…

激情与创造的三重旋律:数学、编程与诗歌翻译的人生奏章

回顾过去的28载&#xff0c;我对生命中那些塑造我成长轨迹的经历充满感恩。自南京大学数学系求学起步&#xff0c;1995年毕业后携带着深厚的数学根基走入社会。从教学数学到转战编程领域&#xff0c;在泸州职业技术学院的教学生涯中逐渐过渡&#xff0c;这一过程仿佛顺理成章&a…

Mysql 中的常用命令

在数字化世界中&#xff0c;数据库已经成为数据存储和处理的核心。而MySQL&#xff0c;作为最受欢迎的关系型数据库管理系统之一&#xff0c;其强大的功能和易用性使它成为开发者和企业的首选。掌握MySQL中的常用命令&#xff0c;是每一位数据库管理员和开发者的基本要求。本篇…

RocketMQ5-01云原生和AI演变下的架构重构

2022年9月22日&#xff0c;迎来 RocketMQ5 的发版&#xff0c;距离 2017 发布的 4.X 时代&#xff0c;RocketMQ 迎来 5.X 时代。 RocketMQ 4.X 时代已经使众多开发者和项目受益&#xff0c;但是随着关注度、使用量逐步上升以及云原生时代的到来&#xff0c;也对其自身架构带来…

无法开机报 不可恢复的错误:securityagent无法创建所要求的机制Teamviewerauthplugin:start

无法开机报 不可恢复的错误&#xff1a;securityagent无法创建所要求的机制Teamviewerauthplugin:start 初步判断很有可能是TeamViewer的某个启动项或者签名书没有&#xff0c; 导致在预加载的时候无法加载TeamViewer。 然后出现这个情况有一个前提&#xff0c;那就是你用了第三…

神经网络框架的基本设计

一、神经网络框架设计的基本流程 确定网络结构、激活函数、损失函数、优化算法&#xff0c;模型的训练与验证&#xff0c;模型的评估和优化&#xff0c;模型的部署。 二、网络结构与激活函数 1、网络架构 这里我们使用的是多层感知机模型MLP(multilayer prrceptron)&#x…