React 02 のコンポーネント通信 - メッセージのサブスクリプションとパブリッシュ、サブスクリプション解除とコンポーネントのアンインストール時のサブスクリプション解除

1 はじめに

1.1 プロップを使用した通信

  • 前回の記事では、コンポーネント間の通信に props を使用することを紹介しましたが、子や兄弟間の通信に props を使用するのは最良の選択ではないため、メッセージのサブスクリプションとパブリッシュを紹介します。
  • props については、次の記事を参照してください:
    React 01 のコンポーネント通信—props

1.2 useEffectについて

  • これは、以下の購読メッセージを作成するときに使用されるため、簡単に説明します。
  • useEffect の使用はクラス コンポーネント内のライフサイクルと同等であり、置き換えることができます。コンポーネントがマウントされます (componentDidMount)コンポーネントが更新されました (componentDidUpdate)コンポーネントはこれら 3 つのフックをアンインストールします (componentWillUnmount)
    • 1⃣️useEffect の第 2 パラメータが空数组ケースであり、第 1 パラメータの関数はクラスコンポーネントの関数と同等です。コンポーネント実装完了フック(componentDidMount)
      この時点で、タイマーを作成したり、メッセージを購読したりすることができます。
    • 2⃣️useEffect の 2 番目のパラメーターが使用される場合不传 或者 是非空数组、最初のパラメーターは次と同等になります。コンポーネント更新フック (componentDidUpdate)
      • 渡されない場合: コンポーネントの状態の各属性を監視し、更新があるたびにそれを呼び出します。
      • 空でない配列: 監視状態で 1 つまたは複数のプロパティを指定でき、監視対象のプロパティが更新された場合にのみ呼び出されます。
    • 3⃣️useEffect 最初のパラメータには戻り値を含めることはできませんが、戻り値がある場合は、戻り関数の場合、この戻り関数はクラス コンポーネント内の関数と同等になります。アンマウントされるコンポーネントのフック (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 単一のトピックをキャンセルする

  • 文法:

    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>
    
  • 次に、その効果を見てみましょう。
    ここに画像の説明を挿入します
  • 最後に簡単な説明をします。
    • コンポーネントをアンインストールすると、クラス コンポーネントの宣言サイクルで、実際にアンインストールするコンポーネントのフックが呼び出されます。関数コンポーネントは useEffect に反映されます。詳細については、上記の 1.2 の導入を参照してください。1.2 useEffectについて》。
    • 実際、上記のアンインストール時に、タイマーをオンにするコードがあり、タイマーをキャンセルしない場合は、問題が発生します。実際、上記でコンポーネント B のみをアンインストールしても、コンポーネント B がサブスクライブされている場合にも問題があると言えます。コンポーネント B をアンインストールしてもサブスクリプションをキャンセルしないのは不合理である場合、これもフロントエンドの最適化の一環として理解できます。
    • 効果が見られないと言う事で、指定したコンポーネントBをアンインストールして観察する方法を紹介します。

4.3.2 指定されたコンポーネントのアンインストール - サブスクリプションのキャンセル

  • 1⃣️ 親コンポーネントで、コンポーネント B の表示を制御します (つまり、親コンポーネントでコンポーネント B のボタンをデザインします卸载/渲染)
    ここに画像の説明を挿入します
  • 2⃣️ コンポーネントB useEffectの第一パラメータのreturn関数に反映されており、コンポーネントBのアンインストール時にreturn関数が実行されるか確認します。
    ここに画像の説明を挿入します
  • 3⃣️効果を確認する
    ここに画像の説明を挿入します
  • 最適化して効果を確認してください。
    つまり、コンポーネントをアンインストールする場合は、サブスクリプションをキャンセルする必要があります、購読解除コードの場所は次のとおりです。
    ここに画像の説明を挿入します
    ここに画像の説明を挿入します

5. 完全なコア コードを添付します。

  • コード構造:
    ここに画像の説明を挿入します
  • コアコード
    • App.js + インデックス.js
      ここに画像の説明を挿入します
    • 親.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;
      
    • コンポーネント –>インデックス.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;
      }
      

おすすめ

転載: blog.csdn.net/suixinfeixiangfei/article/details/132976539