最近复盘了一个同层级两个不同React Node如何访问相互位置并发生关联的问题,限制不能使用Redux、Context、Lifting State Up…
这个问题我当时给出的方案是通过ref
去访问两个component
,虽然此时ref
指定的是一个component instance,但是可以通过访问实例内部的函数去返回具体定位参数(置于组件的state
中)。这样处理虽然能拿到定位信息,但同时也有一个致命问题:无法动态关联,亦或者说没有通知机制。
如果是Lifting State Up方案,我们可以在父级的state
中维护一个中间变量用于存储两个组件定位信息的一方,然后将父层的setState
注入给子组件,在拖动或者一些事件触发的回调中调用,改变父组件的这个状态。同时这个状态可以作为属性传给另一个子组件用于一些位置关联的逻辑处理(完成关联动作)。
且不说当时不能使用这种做法,现在回头看来这种方式也存在种种问题…why?如果说我们的两个子组件内部还嵌套很多层才到实际需要位置信息的DOM,那应用中不断的props
下传就已经足够恶心人了,另外拓展性也不妥。现在是2个组件关联,那如果将来我有N个组件通信逻辑,那维护不也boom了吗?所以我近日思索了一波,估计对方真实意图是想考察设计模式的东西(摊手
发布-订阅
当我们转换思路,采用发布订阅模式来处理这种问题,似乎一切都变得简单了起来…子组件不管嵌套在啥位置,我只需要先在父级订阅对应事件(回调后再根据逻辑置state
),子组件在相应监听回调中主动emit
通知即可,具体实现可以用events
的库(实现Node版 or Facebook实现版),罗列的两者都是为类似浏览器的环境提供一个发布订阅模式的模块。
基本模型如下:
events
中的EventEmitter
可以理解为上图组织的一个工厂实现,其中的on
方法相当于向事件中心发起对对应事件的订阅,emit
方法则相当于发布者触发指定事件并通知事件中心。在我们的项目中大致使用流程是:构建一个模块用于导出EventEmitter
实例;在需要的组件内引入该实例,进行对应的发布订阅;结合Redux的dispatch
进行状态流转。
1 | // emitter.js |
这种设计模式也很容易让我们想到业内的其他两个应用,Vue的响应式,以及Mobx的状态追踪。两者的架构图如下,不多赘述。
观察者
网上有时候会将观察者模式和发布-订阅模式混为一个东西讨论,实际上发布-订阅模式是观察者模式这个广义概念下的一种具体实现,最大的差别在于观察者模式,仅是观察者和主题之间的直接联系,如下图所示:
发布-订阅模式与之相比,在这之间还多了一个“中间人”的角色(见前文图)。这样的好处是什么呢?就是解耦,试想如果没有中间方统筹处理,那么我们组件A和组件B就会在代码中直接存在对彼此的依赖,当场景更为复杂时会出现大量的耦合,造成逻辑关联混乱。发布-订阅模式下,我们只需将事件通讯关联到事件中心一方即可。后续的拓展也将变得清晰。