使用发布订阅模式实现 react 跨组件通信
使用发布订阅模式实现 react 跨组件通信
前言
公司内部一个很大型的项目,有两个毫不相关(组件层级上)的组件产生了联动效果,看上去效果很简单,但是作为一个开发,一眼就能看出来他的实现应该不简单,因为这两个组件之间跨了 N
多的层级,甚至可以说毫不相关的两个组件之间产生了联动。让我产生了浓厚的兴趣。
介绍一下场景
简化一下我们的项目场景,如下图
有两个毫没关系的组件,A
组件中有一个tab
页签,点击tab
就会切换下面对应的内容,A
组件的实现如下
很简单就一个 activeIndex
状态控制显示的内容
B
组件中就3
个按钮
现在的需求就是,点击B
的按钮,A
组件能切换到对应的tab
思考
现在其实就是很简单的组件通信,我们能有巨多的实现方式。但是我们的项目很大,组件跨越的层级都没办法估计,就没有办法去在父级中统一管理状态。
那在仔细的想一下,现在这个需求实际上是需要干什么呢,B
去调 A
的 setState
方法对吧,那我们能不能把 A
组件的方法存在一个地方,然后在 B
中去使用呢?
bus,公共汽车
我们定义一个 bus
对象,啥都没有,就用来在公共的地方存数据
const Bus = {};
export default Bus;
然后在A
组件中将A
的方法存起来,为了让我们这个方法看起来不错,我们可以将存起来的方法命名进行规范一下,比如 组件名称_组件方法名称
然后在B
组件中调用
OK
最终完美实现效果。
这样只是能实现,你要问他好吗,那在review
代码的时候被喷肯定是跑不掉的。那有没有什么优雅的方式存呢?
发布订阅模式
发布订阅模式中,我们主要是需要能订阅事件,取消订阅事件,然后还需要有一个动作触发我们订阅的事件
我们维护一个 状态如下
const MessageState = {
// 当前的状态
state: {},
// 订阅的消息列表
subscriptions: [],
// 更新状态,并且触发订阅的消息
setState(state, skipCallbacks = false) {
this.state = {
...this.state,
...state,
};
if (!skipCallbacks) {
this.subscriptions.forEach((cb) => cb(this.state));
}
},
// 获取状态
getState() {
return this.state;
},
// 订阅一个消息,返回值是取消订阅函数
subscribe(cb) {
this.subscriptions.push(cb);
return () => {
this.unsubscribe(cb);
};
},
// 取消订阅
unsubscribe(cb) {
this.subscriptions = this.subscriptions.filter((item) => item !== cb);
},
};
export default MessageState;
所谓的发布订阅,其实也就是有一个消息列表,订阅就是加一个事件到这个消息列表中,然后有一个时机,我们可以遍历这个消息列表,并且挨个执行他
OK
,那我们看看在代码中应该怎么使用
修改一下我们的A
组件,注册一个事件到我们的消息列表中
在B
组件中,我们去触发一下
OK
,现在我们使用了发布订阅模式优雅的实现了我们的需求了。
注意点
我们的 MessageState
对象需要是单例的!!
然后在使用的时候需要注意一下单一职责,比如我们现在的 MessageState
他的职责主要是为了 A
服务的,复杂的场景下不要只用一个消息对象,要将他们拆分一下。
转载自:https://juejin.cn/post/7242623149752320058