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

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;
      }
      
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: React 组件之间的通信可以使用发布-订阅者模式。这种模式提供了一种不直接对组件进行交互的方法。 在发布-订阅者模式组件之间不直接交互,而是通过一个间的订阅者(也称为事件总线)进行通信。 以下是一个简单的例子: ``` // 创建一个事件总线 const EventBus = new EventEmitter(); // 组件 A 发布消息 function handleClick() { EventBus.emit("message", "Hello from A"); } function ComponentA() { return ( <button onClick={handleClick}> 发布消息 </button> ); } // 组件 B 订阅消息 function ComponentB() { const [message, setMessage] = useState(""); useEffect(() => { EventBus.on("message", setMessage); return () => { EventBus.off("message", setMessage); }; }, []); return <div>收到消息:{message}</div>; } function App() { return ( <div> <ComponentA /> <ComponentB /> </div> ); } ``` 在这个例子组件 A 发布一条消息组件 B 订阅并显示消息。 ### 回答2: 一个例子可以是一个购物车的应用程序。在该应用程序,有一个商品列表组件和一个购物车组件。 当用户点击商品列表的某个商品,商品列表组件会触发一个事件,通知其他组件某个商品被选了。购物车组件订阅了这个事件,并根据收到的消息更新购物车的显示。 具体来说,商品列表组件内部可以定义一个事件发布者,当用户点击某个商品发布者会把该商品的信息发送给所有订阅者。购物车组件可以实现一个订阅者,它会监听商品列表组件的事件,并根据事件的商品信息更新购物车的显示。 例如,当用户点击商品列表的商品「苹果」,商品列表组件就会触发一个事件,事件包含了商品的信息。购物车组件订阅了这个事件,一旦收到事件,它会更新购物车的内容,把「苹果」加入购物车。 这种发布订阅者模式的组件通信方式使得两个组件之间解耦,并且可以方便地添加更多的订阅者。例如,可以添加一个支付模块作为另一个订阅者,当购物车更新,支付模块可以自动计算总金额并展示给用户。 这个例子展示了React组件之间通过发布订阅者模式进行通信的思路,提高了组件的复用性和可扩展性。 ### 回答3: React 组件之间的通信可以通过发布订阅者模式来实现。发布订阅者模式(PubSub)是一种广泛应用于软件设计的模式,它允许组件之间松耦合地进行通信,并且可以在需要的订阅取消订阅消息。 举个例子来说明,假设我们有两个React组件:一个是发布组件(Publisher),另一个是订阅组件(Subscriber)。发布组件发布一个事件,然后订阅组件监听该事件,并在接收到事件后执行相应的操作。 在实际应用,可以使用第三方库来实现发布订阅者模式,比如 PubSub.js。首先,我们需要在发布组件引入 PubSub.js,并在需要发布事件的地方调用`PubSub.publish()`方法,将事件名和相关数据作为参数传入。 ```jsx import React from 'react'; import PubSub from 'pubsub-js'; class Publisher extends React.Component { handleClick = () => { const data = { message: 'Hello, subscribers!' }; PubSub.publish('event', data); }; render() { return ( <button onClick={this.handleClick}> Publish Event </button> ); } } export default Publisher; ``` 然后,在订阅组件也引入 PubSub.js,并在需要订阅事件的地方调用`PubSub.subscribe()`方法,将事件名和对应的回调函数作为参数传入。 ```jsx import React from 'react'; import PubSub from 'pubsub-js'; class Subscriber extends React.Component { state = { message: '', }; componentDidMount() { this.token = PubSub.subscribe('event', this.handleEvent); } componentWillUnmount() { PubSub.unsubscribe(this.token); } handleEvent = (eventName, data) => { this.setState({ message: data.message }); }; render() { return ( <div> <p>{this.state.message}</p> </div> ); } } export default Subscriber; ``` 这样,在订阅组件,每当发布组件发布事件订阅组件就会收到事件,并将相应的消息显示在页面。 通过使用发布订阅者模式,React 组件之间可以实现松耦合的通信,提高了组件的可复用性和可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@素素~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值