• JavaScript 发布-订阅设计模式实现 React EventBus(相当于vue的$Bus)非父子之间通信


    提前声明: 我没有对传入的参数进行及时判断而规避错误,仅仅对核心方法进行了实现;

    解决了react的非父子间的通信;

    参考文档:https://github1s.com/browserify/events/blob/main/events.js

                     https://www.npmjs.com/package/events

                     https://github.com/browserify/events

                     

    1.其中的一种实现的方式   

     

    首先先新建一个文件eventBus.tsx

    class EventBus {
      constructor() {
        this.events = this.events || new Map(); // 储存事件/回调键值对
        this.maxListeners = this.maxListeners || 10; // 设立监听上限
      }
    
      // 触发名为type的事件
      emit(type, ...args) {
        let handler = null;
        handler = this.events.get(type);
        console.log('🚀 ~ file: eventBus.js:11 ~ EventBus ~ emit ~ handler:', handler, args);
        if (handler === undefined) {
          return false;
        }
        if (Array.isArray(handler)) {
          // 如果是一个数组说明有多个监听者,需要依次此触发里面的函数
          // eslint-disable-next-line no-plusplus
          for (let i = 0; i < handler.length; i++) {
            if (args.length > 0) {
              handler[i].apply(this, args);
            } else {
              handler[i].call(this);
            }
          }
        } else {
          // 单个函数的情况我们直接触发即可
          // eslint-disable-next-line no-lonely-if
          if (args.length > 0) {
            handler.apply(this, args);
          } else {
            handler.call(this);
          }
        }
        return true;
      }
    
      // 监听名为type的事件
      on(type, fn) {
        const handler = this.events.get(type);
        if (!handler) {
          this.events.set(type, fn);
        } else if (handler && typeof handler === 'function') {
          // 如果handler是函数说明只有一个监听者
          this.events.set(type, [handler, fn]); // 多个监听者我们需要用数组储存
        } else {
          handler.push(fn); // 已经有多个监听者,那么直接往数组里push函数即可
        }
      }
    
      // eslint-disable-next-line consistent-return
      remove(type, fn) {
        const handler = this.events.get(type); // 获取对应事件名称的函数清单
        if (handler && typeof handler === 'function') {
          // 如果是函数,说明只被监听了一次
          this.events.delete(type, fn);
        } else {
          if (handler === undefined) {
            return false;
          }
          let position = null;
          // 如果handler是数组,说明被监听多次要找到对应的函数
          // eslint-disable-next-line no-plusplus
          for (let i = 0; i < handler.length; i++) {
            if (handler[i] === fn) {
              position = i;
            } else {
              position = -1;
            }
          }
          // 如果找到匹配的函数,从数组中清除
          if (position !== -1) {
            // 找到数组对应的位置,直接清除此回调
            handler.splice(position, 1);
            // 如果清除后只有一个函数,那么取消数组,以函数形式保存
            if (handler.length === 1) {
              this.events.set(type, handler[0]);
            }
          } else {
            return this;
          }
        }
      }
    }
    
    const eventBus = new EventBus();
    export default eventBus;
    
    
    // 简单实现的发布订阅模式 也是对的
    // class EventEmitter {
    //   constructor() {
    //     this.eventBus = this.eventBus || {};
    //   }
    
    //   emit(type, params) {
    //     this.eventBus.type.forEach(item => {
    //       item(params);
    //     });
    //   }
    
    //   on(type, fn) {
    //     if (this.eventBus.type) {
    //       this.eventBus.type.push(fn);
    //     } else {
    //       this.eventBus.type = [fn];
    //     }
    //   }
    
    //   remove(type, fn) {
    //     if (this.eventBus[type]) {
    //       delete this.eventBus.type
    //     }
    //   }
    // }
    
    // const eventBus = new EventEmitter();
    // export default eventBus;

    然后再组件A使用=>接收

    import React, { useState, useEffect, useCallback } from 'react';
    // import eventBus from '@/utils/events';
    import eventBus  from '@/utils/eventBus';
    
    const TopBox = () => {
      const [num, setNum] = useState(0);
    
      const addNum = useCallback((message: any) => {
        setNum(message);
      }, []);
    
      useEffect(() => {
        eventBus.on('emit', addNum);
        return () => {
          if(eventBus) {
            eventBus.remove('emit', addNum);
          }
        };
      }, []);
    
      return (
        <div>
          <span>我也不知道是什么111: {num}span>
        div>
      );
    };
    
    export default TopBox;

    然后再组件B使用=>触发

    import React, { useState } from 'react';
    import { Button } from 'antd';
    // import eventBus from '@/utils/events';
    import eventBus from '@/utils/eventBus';
    
    const TopBox = () => {
      const [num,setNum] = useState(0)
    
      const addNum = () => {
        const numNew = num + 1;
        setNum(numNew);
        eventBus.emit('emit', numNew)
      }
    
      return (
        <div>
          <Button onClick={() => {addNum()}}>按钮Button>
          <span>我也不知道是什么:{num}span>
        div>
      );
    };
    
    export default TopBox;

     

     

    2.其中的另一种实现的方式

    安装这个events插件

    yarn add events

    新建一个文件evnets.ts

    import { EventEmitter } from 'events';
    export default new EventEmitter();

    组件A使用

    import React, { useState, useEffect, useCallback } from 'react';
    import eventBus from '@/utils/events';
    // import eventBus  from '@/utils/eventBus';
    
    const TopBox = () => {
      const [num, setNum] = useState(0);
    
      const addNum = useCallback((message: any) => {
        setNum(message);
      }, []);
    
      useEffect(() => {
        eventBus.on('emit', addNum);
        return () => {
          if(eventBus) {
            // eventBus.remove('emit', addNum);
            eventBus.removeListener('emit', addNum);
          }
        };
      }, []);
    
      return (
        <div>
          <span>我也不知道是什么111: {num}span>
        div>
      );
    };
    
    export default TopBox;

    组件B使用

    import React, { useState } from 'react';
    import { Button } from 'antd';
    import eventBus from '@/utils/events';
    // import eventBus from '@/utils/eventBus';
    
    const TopBox = () => {
      const [num,setNum] = useState(0)
    
      const addNum = () => {
        const numNew = num + 1;
        setNum(numNew);
        eventBus.emit('emit', numNew)
      }
    
      return (
        <div>
          <Button onClick={() => {addNum()}}>按钮Button>
          <span>我也不知道是什么:{num}span>
        div>
      );
    };
    
    export default TopBox;

     

  • 相关阅读:
    “揭秘淘宝店铺所有商品接口:一键获取海量热销宝贝信息!“
    Spring+SpringMVC+Jsp实现新冠肺炎疫苗接种管理系统
    力扣刷题记录162.1-----127. 单词接龙
    GO 中的时间操作(time & dateparse)【GO 基础】
    Mybatis传递实体对象只能直接获取,不能使用对象.属性方式获取
    qml Textinput 、TextField、TextEdit、TextArea用法介绍
    java使用elasticsearchClient调用es7.17 - 新增数据,基本数据查询,条件筛选查询
    进了985材料天坑,还刚得知转专业特别难,应该怎么办?
    ggplot2画各种误差线和森林图
    ESXi 6.7添加螃蟹2.5g网卡支持
  • 原文地址:https://www.cnblogs.com/dreamtt/p/17328874.html