react不同组件中方法监听--eventProxy发布-订阅模式

1、需求问题:
在这里插入图片描述

在做React-ant-design-mobileAPP项目的时候。当导航栏中的确定重置事件需要刷新列表页。而这两个组件毫无联系。这时候就用到了eventProxy来监听不同组件的方法函数。
2、

  • on、one:on 与 one 函数用于订阅者监听相应的事件,并将事件响应时的函数作为参数,on 与 one 的唯一区别就是,使用 one 进行订阅的函数,只会触发一次,而 使用 on 进行订阅的函数,每次事件发生相应时都会被触发。
  • trigger:trigger 用于发布者发布事件,将除第一参数(事件名)的其他参数,作为新的参数,触发使用 one 与 on 进行订阅的函数。
  • off:用于解除所有订阅了某个事件的所有函数。

3、首先需要引入eventProxy文件。
单独放在一个文件中,用script引入,或important入即可使用eventProxy;

const eventProxy = {
    
    
  onObj: {
    
    },
  oneObj: {
    
    },
  on: function (key, fn) {
    
    
    if (this.onObj[key] === undefined) {
    
    
      this.onObj[key] = [];
    }
    const args = [].concat(Array.prototype.slice.call(arguments, 1));
    for (let i = 0; i < args.length; i++) {
    
    
      this.onObj[key].push(args[i]);
    }
  },
  one: function (key, fn) {
    
    
    if (this.oneObj[key] === undefined) {
    
    
      this.oneObj[key] = [];
    }

    this.oneObj[key].push(fn);
  },
  off: function (key) {
    
    
    this.onObj[key] = [];
    this.oneObj[key] = [];
  },
  trigger: function () {
    
    
    let key, args;
    if (arguments.length == 0) {
    
    
      return false;
    }
    key = arguments[0];
    args = [].concat(Array.prototype.slice.call(arguments, 1));
    if (this.onObj[key] !== undefined
      && this.onObj[key].length > 0) {
    
    
      for (let i in this.onObj[key]) {
    
    
        this.onObj[key][i].call(null, args[i]);
      }
    }
    if (this.oneObj[key] !== undefined
      && this.oneObj[key].length > 0) {
    
    
      for (let i in this.oneObj[key]) {
    
    
        this.oneObj[key][i].apply(null, args);
        this.oneObj[key][i] = undefined;
      }
      this.oneObj[key] = [];
    }
  }
};

export default eventProxy;

4、使用

  • 在需要监听的组件方法中:
  • import eventProxy from '../../utils/eventProxy' 引入文件
  • 在需要监听的方法中
    我这里是确定事件
  queryClick = () => {
    
    
   eventProxy.trigger('query', 'list')
    this.setState({
    
    
      visible: false,
    })
  }

  • 然后在需要执行相应动作的组件中去使用它
  • componentDidMount中去使用

  componentDidMount() {
    
    
    // 清除事件监听
    eventProxy.on('query', () => {
    
    
      this.getDynamicsList()
    })
    // eventProxy.on('clear', () => {
    
    
    //   this.getDynamicsList()
    // })
  }

使用后一定要在组件卸载的时候清除事件监听

  componentWillUnmount = () => {
    
    
    // 清除监听
    eventProxy.off('query', '');
  }

5、就这样,将不同组件方法和事件联系起来了。

可参考—外观模式

外观模式,是一种相对简单而又无处不在的模式。外观模式提供一个高层接口,这个接口使得客户端或子系统更加方便调用。 用一段再简单不过的代码来表示:

实现一个简单的订阅发布者
观察者模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
事实上,只要你曾经在DOM节点上绑定过事件函数,那么你就曾经使用过观察者模式了!
document.body.addEventListener(‘click’, function () {
alert(2);
});

但是这只是对观察者模式最简单的使用,在很多场景下我们经常会实现一些自定义事件来满足我们的需求。
举个例子:
你去一家公司应聘,谈了一顿下来,hr跟你说:“好了,你回去等通知吧!”。
这个时候,1.你会问公司的电话,然后每天打过去问一遍结果
2.把自己的手机号留给hr,然后等他给你打电话

相信很多时候呢,大家都是选择了后者。
万一你每天给hr打电话弄烦他了,或许他本来打算招你的,现在也不再打算再鸟你啦!
那么这个时候,hr就相当于一个发布者,而你就是一个订阅者啦!
好吧,大部分叫你回去等消息的就等于没救啦…
我还遇到过一个如果你没被录取,就连通知都不通知你的公司!
那么一个简单的观察者模式应该怎么实现呢?
要指定一个发布者;
给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者;(这家公司很多人来应聘)
最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数;(你up or 你over)

var event = {
    
    }; //发布者(hr)
event.clietList = []; //发布者的缓存列表(应聘者列表)
event.listen = function(fn) {
    
     //增加订阅者函数
this.clietList.push(fn);
};
event.trigger = function() {
    
     //发布消息函数
for (var i = 0; i < this.clietList.length; i++) {
    
    
    var fn = this.clietList[i];
    fn.apply(this, arguments);
}
};
event.listen(function(time) {
    
     //某人订阅了这个消息
console.log('正式上班时间:' + time);
});
event.trigger('2016/10',yes); //发布消息
//输出 正式上班时间:2016/10
到这里,我们已经实现了一个最简单的观察者模式了!
但是上面的函数其实存在一个问题,那就是发布者没办法选择自己要发布的消息类型!
比如这家公司同时在招php,web前端,如果使用上面的函数就没办法区分职位了!只能一次性把全部订阅者都发送一遍消息。
对上面的代码进行改写:
var event = {
    
    }; //发布者(hr)
event.clietList = []; //发布者的缓存列表(应聘者列表)
event.listen = function(key, fn) {
    
     //增加订阅者函数
if (!this.clietList[key]) {
    
    
    this.clietList[key] = [];
}
this.clietList[key].push(fn);
};
event.trigger = function() {
    
     //发布消息函数
var key = Array.prototype.shift.call(arguments),
    fns = this.clietList[key];
for (var i = 0; i < fns.length; i++) {
    
    
    var fn = fns[i];
    fn.apply(this, arguments);
}
};
event.listen('web前端', fn1 = function(time) {
    
     //小强订阅了这个消息。
console.log('姓名:小强');
console.log('正式上班时间:' + time);
});
event.listen('web前端', fn2 = function(time) {
    
     //大大强订阅了这个消息
console.log('姓名:大大强');
console.log('正式上班时间:' + time);
});
//发布者发布消息
event.trigger('web前端','小强', '2016/10'); //姓名:小强 正式上班时间:2016/10
event.trigger('php','大大强', '2016/15'); //姓名:大大强 正式上班时间:2016/15
通过添加了一个key,我们实现了对职位的判断。
有了订阅事件,我们怎么能少了取消订阅事件呢?
event.remove = function(key, fn) {
    
    
var fns = this.clietList[key];
if (!fns) {
    
    
    return false;
}
if (!fn) {
    
     //如果没有传入fn回调函数,直接取消key对应消息的所有订阅
    this.clietList[key] = [];
} else {
    
    
    for (var i = 0; i < fns.length; i++) {
    
     //遍历回调函数列表
        var _fn = fns[i];
        if (_fn === fn) {
    
    
            fns.splice(i, 1); //删除订阅者的回调函数
        }
    }
}
};
//这时候必须指定回调函数,否则无法在remove函数中进行对比删除。
event.listen('web前端', fn1 = function(time) {
    
     //小强订阅了这个消息。
console.log('姓名:小强');
console.log('正式上班时间:' + time);
});
event.listen('web前端', fn2 = function(time) {
    
     //大大强订阅了这个消息
console.log('姓名:大大强');
console.log('正式上班时间:' + time);
});
event.remove('web前端',fn1);
//发布者发布消息
event.trigger('web前端','2016/10');
//输出 姓名:大大强 正式上班时间:2016/10

对上面代码进行改进,创建一个全局对象来实现观察者模式,
使用闭包实现私有变量,仅暴露必须的API给使用者:

var event = (function() {
    
    
var clietList = []; //发布者的缓存列表(应聘者列表)

var listen = function(key, fn) {
    
     //增加订阅者函数
    if (!this.clietList[key]) {
    
    
        this.clietList[key] = [];
    }
    this.clietList[key].push(fn);
};

var trigger = function() {
    
     //发布消息函数
    var key = Array.prototype.shift.call(arguments),
        fns = this.clietList[key];
    for (var i = 0; i < fns.length; i++) {
    
    
        var fn = fns[i];
        fn.apply(this, arguments);
    }
};

var remove = function(key, fn) {
    
    
    var fns = this.clietList[key];
    if (!fns) {
    
    
        return false;
    }
    if (!fn) {
    
     //如果没有传入fn回调函数,直接取消key对应消息的所有订阅
        this.clietList[key] = [];
    } else {
    
    
        for (var i = 0; i < fns.length; i++) {
    
     //遍历回调函数列表
            var _fn = fns[i];
            if (_fn === fn) {
    
    
                fns.splice(i, 1); //删除订阅者的回调函数
            }
        }
    }
};

return{
    
    
    listen:listen,
    trigger:trigger,
    remove:remove
}
})();
观察者模式进阶:
使用命名空间防止事件名冲突
实现先发布后订阅功能
[观察者模式引自原作者](https://segmentfault.com/a/1190000023447952) 

猜你喜欢

转载自blog.csdn.net/weixin_45416217/article/details/107702580