React中组件通信02——消息订阅与发布、取消订阅以及卸载组件时取消订阅

2023-09-18 21:36:45

1. 前言

1.1 使用props通信

  • 上一篇介绍了使用props进行组件之间的通信,但是对于子传父和兄弟之间的通信使用props不是最好的选择,所以介绍一下消息订阅与发布。
  • 关于props,可以看下面的文章,如下:
    React中组件通信01——props.

1.2 关于useEffect

  • 下面写订阅消息的时候会用到,所以这里简单介绍一下:
  • useEffect 的使用,可以相当于class组件中的生命周期,可以代替组件挂载完毕(componentDidMount)组件更新完毕(componentDidUpdate)组件将要卸载这三个钩子(componentWillUnmount)
    • 1⃣️useEffect 的第二个参数是空数组的情况,第一个参数里的函数就相当于是class组件中的组件挂载完毕钩子(componentDidMount)
      这时可以做定时器、订阅消息等。
    • 2⃣️useEffect 的第二个参数如果不传 或者 是非空数组,此时第一个参数相当于组件更新完毕的钩子(componentDidUpdate)
      • 不传:监测组件中state里的每个属性,只要有更新就会调用;
      • 非空数组:可以指定监测state里的某一个或某几个属性,只有监测的属性有更新才会调用。
    • 3⃣️useEffect 第一个参数可以没有返回值,但是如果有返回函数,则这个返回函数相当于class组件中的组件将要卸载的钩子(componentWillUnmount)
      一般用于取消定时器、取消订阅等。

2. 安装 pubsub-js

  • 命令如下:
    npm install pubsub-js
    

3. 消息订阅与发布

3.1 简单例子-1

  • 小需求设计:
    在这里插入图片描述
  • 代码设计实现
    • ChildA——发布消息
      核心代码就2行:
      import PubSub from 'pubsub-js'
      
      //PubSub.publish('MY TOPIC', 'hello world!');
      PubSub.publish('GamesNews','通知:在我校(ChildA)举办运动会的时间定于10月16日');
      
      在这里插入图片描述
    • ChildB——订阅消息(借助于 useEffect 钩子),核心代码如下:
      import PubSub from 'pubsub-js'
      
          //订阅运动会消息
      const subscriberGamesNew = function(msg, data){
          console.log('ChildB 订阅的消息--subscriberGamesNew---');
          console.log( msg, data );//msg-订阅的topic  data-消息内容
      }
      
      useEffect(()=>{
          //subscribe-订阅的方法   'GamesNews'-订阅的主题
          let token = PubSub.subscribe('GamesNews', subscriberGamesNew);
          console.log('token',token);
      },[])//这里第二个参数是空数组 [],这种情况相当于class组件中 "组件挂载完毕的钩子" 
      
      在这里插入图片描述
  • 效果展示
    在这里插入图片描述

3.2 简单例子-2(完善、优化)——订阅消息+使用消息

  • 小需求如下:
    在这里插入图片描述
  • 代码设计如下:
    • 发布方
      在这里插入图片描述
    • 订阅方
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
  • 效果如下:
    在这里插入图片描述

4. 取消订阅

4.1 取消单个topic

  • 语法:

    const token1 = PubSub.subscribe('GamesNews', mySubscribers);//订阅 GamesNews
    
    //取消订阅
    PubSub.unsubscribe(token1);
    或
    PubSub.unsubscribe('GamesNews');
    
  • 例子如下:
    在这里插入图片描述

  • 效果如下:
    在这里插入图片描述

  • 上面函数可以优化一下,根据主题取消,如下:

     const myUnsubscribe =(topic)=>{
         PubSub.unsubscribe(topic); //取消订阅的GamesNews
         console.log('取消订阅运动会消息---成功!!!');
     }
    
    <button onClick={()=>myUnsubscribe('GamesNews')}>取消订阅运动会消息</button>
    

4.2 取消多个或更多语法

4.3 卸载组件时取消订阅

4.3.1 卸载所有组件

  • 首先,给出卸载所有组件的代码,如下:
    在这里插入图片描述
    核心代码就这一丢丢,不说了,自己看
    import root from '../index';
    
        //卸载组件---卸载所有
        const unMountAll =()=>{
            //卸载组件 root
            root.unmount();
        }
    
    
     <button onClick={unMountAll}>卸载所有组件</button>
    
  • 然后,再看效果:
    在这里插入图片描述
  • 最后,简单说明一下:
    • 卸载组件,在class组件的声明周期中,其实是调用了组件将要卸载的钩子,函数组件中可以在useEffect中体现,详细看上面1.2的介绍《1.2 关于useEffect》。
    • 其实在上面卸载时,如果有代码有开启定时器而又没有取消定时器的话,是有问题的,其实也可以这么说,如果上面只卸载了B组件,但是B组件有有订阅消息,那么如果卸载了B组件,但是订阅没有取消的话,是不合理的,这也可以理解是前端优化的部分。
    • 光说看不出效果,下面我门就介绍卸载指定组件B来观察观察。

4.3.2 卸载指定组件——取消订阅

  • 1⃣️ 在父组件中,控制B组件展示(即:父组件中,设计一个卸载/渲染B组件的按钮)
    在这里插入图片描述
  • 2⃣️ 在B组件 useEffect 中第一参数的返回函数中,有所体现,看卸载B组件时,是否执行返回函数
    在这里插入图片描述
  • 3⃣️ 看效果
    在这里插入图片描述
  • 优化,看效果:
    所以,需要在卸载组件时,把订阅取消了,取消订阅的代码所放位置,如下:
    在这里插入图片描述
    在这里插入图片描述

5. 附核心完整代码

  • 代码结构:
    在这里插入图片描述
  • 核心代码
    • App.js + index.js
      在这里插入图片描述
    • Parent.jsx
      import React from "react";
      import ChildA from "./ChildA";
      import ChildB from "./ChildB";
      import './index.css'
      import root from '../index';
      
      function Parent() {
      
          const [mountChildBFlag,setMountChildFlag] = React.useState(true);
      
          //卸载组件---卸载所有
          const unMountAll =()=>{
              //卸载组件 root
              root.unmount();
          }
          return(
              <div className="parent">
                  我是父组件!
      
                  <div className="childA">
                      <ChildA notice={'通知——今天放假!'}/>
                  </div>
      
                  {/* <div className="childB">
                      <ChildB notice={'通知——今天放假!'} />
                  </div> */}
      
                  {/* 这里根据 mountChildBFlag 控制B组件的状态 */}
                  {
                      mountChildBFlag ? 
                      <div className="childB">
                          <ChildB notice={'通知——今天放假!'} />
                      </div>
                      : ""
                  }
      
                  <br /><br />
                  <button onClick={()=>setMountChildFlag(!mountChildBFlag)}>卸载B组件/渲染B组件</button>
      
                  <br /><br />
                  <button onClick={unMountAll}>卸载所有组件</button>
                  
              </div>
          )
      }
      export default Parent;
      
    • ChildA.jsx
      import React from "react";
      import PubSub from 'pubsub-js'
      
      function ChildA(props){
      
          const stuNameRef = React.useRef();
      
          //发布运动会消息 按钮触发
          function publishGamesNews(){
              // 发布运动会消息  topic-->GamesNews
              PubSub.publish('GamesNews','通知:在我校(ChildA)举办运动会的时间定于10月16日');
              console.log('-----ChildA 发布了 GamesNews 消息----');
          }
          // 发布学生消息  开除的学生
          function expelStuSubmit(event){
              event.preventDefault();//非受控组件  只取表单数据,但阻止表单提交,实现页面无刷新
      
              const stuName = stuNameRef.current.value;
              PubSub.publish('stusInfo',{stuName:stuName,schoolName:'ChildA-School',stuState:'被开除'});
          }
      
          return(
              <div>
                  我是子组件A!!!
                  <br /><br />
                  收到来自于父组件的数据:{props.notice}
      
                  <br /><br />
                  <button onClick={publishGamesNews}>发布运动会消息</button>
      
                  <br /><br />
                  <form onSubmit={expelStuSubmit}>
                      学生姓名:<input type="text" ref={stuNameRef} name="stuName"/>
                      <button>开除学生</button>
                  </form>
              </div>
          )
      }
      
      export default ChildA;
      
    • ChildB.jsx
      import PubSub from 'pubsub-js'
      import { useEffect,useState } from 'react';
      
      function ChildB(props){
      
          const [gamesNews,setGamesNews] = useState('等通知……');
          const [stusInfo,setStuInfo] = useState(
              [
                  {stuName:'学生1',schoolName:'ChildA-School',stuState:'在校'},
                  {stuName:'学生2',schoolName:'ABC学校',stuState:'离校'},
                  {stuName:'张三',schoolName:'ChildA-School',stuState:'在校'},
                  {stuName:'李四',schoolName:'XXX附属中学',stuState:'托管'},
                  {stuName:'王五',schoolName:'ChildA-School',stuState:'在校'},
              ]
          );
      
          //我的订阅方法
          const mySubscribers = function(msg, data){
              console.log('ChildB 订阅的消息--mySubscribers---');
              // console.log( msg, data );//msg-订阅的topic  data-消息内容
              //将订阅到的新消息进行更新
              if('GamesNews'===msg){
                  console.log('订阅到运动会的消息是:',data);
                  setGamesNews(data);
              }else if('stusInfo'===msg){
                  console.log('订阅到开除的学生是:',data);
                  // const newStuInfo = [...stusInfo,data];//这个不去重,追加数据
                  //这个地方需要注意stusInfo 和 data的类型
                  const newStuInfo = stusInfo.map((stu)=>{
                      return data.stuName===stu.stuName ? data : stu;
                  });
                  setStuInfo(newStuInfo);
              }
          }
      
          useEffect(()=>{
              //subscribe-订阅的方法  
              const token1 = PubSub.subscribe('GamesNews', mySubscribers);//订阅 GamesNews
              const token2 = PubSub.subscribe('stusInfo', mySubscribers);// 订阅 stusInfo
              console.log('token1---',token1);
              console.log('token2---',token2);
      
              return ()=>{
                  //这个返回函数,相当于class中的“组件将要卸载的钩子”  在这里可以取消订阅
                  console.log('ChildB组件...被卸载了');
      
                  PubSub.unsubscribe(token1); //取消订阅
                  
              }
          },[])//这里第二个参数是空数组 [],这种情况相当于class组件中 "组件挂载完毕的钩子" 
      
          //取消订阅
          const myUnsubscribe =(topic)=>{
              PubSub.unsubscribe(topic); //取消订阅的GamesNews
              console.log('取消订阅运动会消息---成功!!!');
          }
      
          return(
              <div>
                  我是子组件B!!!
                  <br /><br />
                  收到来自于父组件的数据:{props.notice}
      
                  <br /><br /><br />
                  订阅1——运动会消息:{gamesNews}
                  <br />
                  <button onClick={()=>myUnsubscribe('GamesNews')}>取消订阅运动会消息</button>
      
                  <br /><br /><br />
                  订阅2——学生消息:
                  <table>
                      <thead>
                          <tr>
                              <th>学生姓名</th>
                              <th>所在学校</th>
                              <th>状态</th>
                          </tr>
                      </thead>
                      <tbody>
                          {stusInfo.map((stu,index)=>{
                              return <tr key={index}>
                                  <td>{stu.stuName}</td>
                                  <td>{stu.schoolName}</td>
                                  <td>{stu.stuState}</td>
                              </tr>
                          })}
                      </tbody>
                  </table>
      
      
              </div>
          )
      }
      
      export default ChildB;
      
    • component–>index.css
      .parent{
          background-color: blueviolet;
          border: 1px solid;
          height: 900px;
          width: 600px;
          text-align: center;
      }
      
      .childA{
          background-color: green;
          height: 170px;
          margin: 20px;
      }
      
      .childB{
          background-color: grey;
          height: 400px;
          margin: 20px;
      }
      
更多推荐

CentOS 7系统安装与配置、常用100条操作命令

CentOS7是一个广泛使用的开源Linux操作系统,它是RedHatEnterpriseLinux(RHEL)的一个免费重建版本,以稳定性和安全性而著称。在CentOS7上安装虚拟机通常使用虚拟化技术,如VirtualBox或VMware等。以下是CentOS7的简要介绍以及如何安装CentOS7虚拟机的步骤。Cen

git 的文件目录错误删除 --chatGPT

问:git的文件目录错误删除,需要还原到最后一次提交的位置,如何操作gpt:如果您在Git中删除了文件或目录,想要还原到最后一次提交的位置,可以使用以下步骤:1.**查看Git状态**:首先,可以使用以下命令来查看当前Git仓库的状态,以确保您删除了哪些文件或目录:```gitstatus```这将列出未提交的更改,包

(python语言程序设计教程)自学二

(python语言程序设计教程)自学二文章目录前言一、编写简单的程序1.1.标识符及命名规则1.2.变量与赋值语句1.3.数值1.4.字符串二、turtle画图2.1.绘制爱心并书写文本2.2.绘制幸运的四叶草2.3.浪漫的玫瑰花三、课后习题总结前言本系列文章,主要是对学校开设的python课程进行总结,教科书为:py

性能测试 —— 性能测试常见的测试指标 !

一、什么是性能测试先看下百度百科对它的定义,性能测试是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。我们可以认为性能测试是:通过在测试环境下对系统或构件的性能进行探测,用以验证在生产环境下系统性能是否达到预估的性能需求,发现系统可能存在的性能瓶颈,进而改善优化并系统的性能,提高系

多线程的上下文切换

多线程的上下文切换是指在多线程环境下,操作系统或调度器将CPU执行权从一个线程切换到另一个线程的过程。上下文切换允许多个线程交替执行,使得看起来多个线程同时在运行,从而实现并发性。上下文切换的发生通常有以下几种情况:时间片耗尽:操作系统为每个线程分配一定的时间片(或时间量),当一个线程的时间片用尽时,操作系统会暂停该线

【面试题精讲】如何将二进制转为十六进制

有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准https://blog.zysicyj.top首发博客地址文章更新计划系列文章地址/***二进制转换为十六进制*这里主要用于处理图片数据,因为数据库存储了图片的Base64编码*/privateStringbytesToHexString(by

CVPR 2023 | UniMatch: 重新审视半监督语义分割中的强弱一致性

在这里和大家分享一下我们被CVPR2023录用的工作"RevisitingWeak-to-StrongConsistencyinSemi-SupervisedSemanticSegmentation"。在本工作中,我们重新审视了半监督语义分割中的“强弱一致性”方法。我们首先发现,最基本的约束强弱一致性的方法FixMat

数据结构:线性表之-队列

目录什么是队列?详解:功能介绍代码实现定义队列基本结构1,初始化2,销毁3,尾入数据4,头出数据5,取队头的数据6,取队尾的数据7,判断是否为空8,计算队列中的元素成品Queue.hQueue.ctest.c队列的讲解将建立在有双向链表的基础之上进行讲解当然队列只是链表的一种分支单项链表详解:#数据结构:线性表之-单向

Spring底层原理之 BeanFactory 与 ApplicationContext

🐌个人主页:🐌叶落闲庭💨我的专栏:💨c语言数据结构javaEE操作系统Redis石可破也,而不可夺坚;丹可磨也,而不可夺赤。Spring底层原理一、BeanFactory与ApplicationContext二、BeanFactory功能三、ApplicationContext功能3.1getMessage3.

【计算机网络】IP协议第一讲(协议格式介绍)

IP协议1.协议头格式1.1概念介绍1.2补充说明1.2.18位生存时间---TTL1.2.216位首部检验和首先明确一个概念:TCP/IP协议是配合使用的,TCP负责可靠传输策略,IP则是负责传输,TCP协议是位于传输层提供的是策略解决可靠性问题,IP协议在网络层,提供的是网络传输服务,实现A主机到B主机的跨网络通信

Python爬虫异常处理实用技巧分享

当我们编写爬虫程序时,经常会遇到各种各样的异常情况,比如网络连接失败、页面解析错误、请求被拒绝等等。这些异常情况可能导致程序中断或者无法正常运行,给我们的数据采集工作带来一定的困扰。所以,掌握一些实用的异常处理技巧对于提高爬虫的稳定性和效率非常重要。在Python中,我们可以使用try-except语句来处理异常。下面

热文推荐