react 中发布订阅模式使用
react 中发布订阅模式使用
场景
怎么能将设计模式应用到我们的 React 项目中?以前一直在思考这个问题。
场景一
模块 A 模块 B 需要用到同一个数据 data,A 和 B 都会修改这份数据,且这两个模块会同时存在;这时我们如何做到数据公用与各个模块的更新?
方案一:
将这份数据作为公共的数据 data,A B 模块同时使用并更改这份数据这一份数据。若使用 redux 代码可能是这样:
// store
const store = {
common: { data: [] },
A: {},
B: {},
};
// reducer
function commonReducer(state = { data: [] }, action) {
switch (action.type) {
case 'common_setData': {
return {
...state,
data: action.data,
};
}
default:
return state;
}
}
// connect
const actionCreator = () => {};
connect(({ A, common }) => ({ ...A, data: common.data }))(A);
connect(({ B, common }) => ({ ...A, data: common.data }))(B);
// change
// A B change调用方法;
this.props.dispatch({
type: 'common_setData',
data: [1, 2],
});
好的,第一种场景可以使用 redux 完美解决
方案二:待补充
场景二
A 模块使用了 data1, B 模块使用了 data2;A B 模块可以修改对应的 data;这两份 data 结构上不同,但是存在业务上的联系: 当 data1 更新后需要 data2 更新;data2 更新同样需要 data1 同步;对应后端的两个不同的 API。
我们整理一下
- A B 使用两份存在联系的 data
- 其中一个更新需要另一个更新
- 两份 data 对应不同的 API 接口
- A B 对应两个不同的 tab 且可能同时存在
方案一
当其中一个数据因操作发生更新时,判断另一个模块是否存在 如果存在则调用他的数据更新逻辑;
如果你使用了 redux,可能方便一点:
// reducerA
// 省略B
function reducerA(state = { data: [] }, action) {
switch(action.type) {
case 'A_setDataA': {
return {
...state,
data: action.data
}
}
default: return state
}
}
// 假设使用了thunk中间件
const queryA = () => async (dispatch, getState) => {
const dataA = await API.queryA()
dispatch({
type: 'A_setDataA'
data: dataA
})
}
// page
class B extends React.Component {
handleUpdateData = () => {
// 如果 A模块存在
const { isAExistFlag, dispatch, queryA, queryB } = props
dispatch(queryB())
if (isAExistFlag) {
dispatch(queryA())
}
}
}
这样利用了 redux 可以实现功能,在模块 B 内调用模块 A 的更新逻辑;但这样逻辑就耦合了,我在模块 A 调用模块 B 方法 在模块 B 调用模块 A 的方法;但很有可能这两个模块是没有其他交互的。这违反了低耦合高内聚的原则
而且书写 redux 的一个原则就是 不要调用(dispatch)其他模块的 action
如果你不使用 redux 如果是一个模块内调用其他模块的方法也是没有做到解耦的;那如何做到解耦尼?请看方案二
方案二:利用事件系统
如果您的项目中没有一个全局的事件系统,可能需要引入一个;一个简单的事件系统大概是:
class EventEmitter {
constructor() {
this.listeners = {};
}
on(type, cb, mode) {
let cbs = this.listeners[type];
if (!cbs) {
cbs = [];
}
cbs.push(cb);
this.listeners[type] = cbs;
return () => {
this.remove(type, cb);
};
}
emit(type, ...args) {
console.log(
`%c event ${type} be triggered`,
'color:rgb(20,150,250);font-size:14px',
);
const cbs = this.listeners[type];
if (Array.isArray(cbs)) {
for (let i = 0; i < cbs.length; i++) {
const cb = cbs[i];
if (typeof cb === 'function') {
cb(...args);
}
}
}
}
remove(type, cb) {
if (cb) {
let cbs = this.listeners[type];
cbs = cbs.filter(eMap => eMap.cb !== cb);
this.listeners[type] = cbs;
} else {
this.listeners[type] = null;
delete this.listeners[type];
}
}
}
export default new EventEmitter();
这个事件系统具有注册,发布,移除事件的功能。那我们怎么在刚才这个场景去使用它尼?
- 发布:当A模块内数据因操作发生变化时,触发该数据变化的事件,定义
type为data1Change; - 注册:这里B模块的注册的时机,上述的场景为A和B模块可能同时出现,所以A模块存在B模块却不存在。所以这个B模块事件的监听选择在B模块组件的
componentDidMount的时候注册,在componentWillUnmount时移除
大致的代码如下:
import EventEmitter from 'eventEmitter'
class A extends React.Component {
handleUpdateData = () => {
// 如果 A模块存在
const { dispatch, queryB } = props
dispatch(queryA())
EventEmitter.emit('data1Change')
}
}
// B
import EventEmitter from 'eventEmitter'
class B extends React.Component {
componentDidMount() {
const unlistener = EventEmitter.on('data1Change', this.handleData1Change)
}
componentWillUnmount() {
EventEmitter.on('data1Change', this.handleData1Change)
}
handleData1Change = () => {
const { dispatch, queryB } = this.props
dispatch(queryB())
}
}
这样通过事件系统做到了两个模块之间的解耦,作为事件发布方只管发布自己的事件。两个模块在事件系统唯一的联系就是事先定义好事件的type。
不过这也增加了几行的代码量,但相比带来的优势来说可以不计。
其他方案欢迎大家评论
其他场景
待大家补充
来源:https://segmentfault.com/a/1190000017194450
react 中发布订阅模式使用的更多相关文章
- 理解JavaScript设计模式与开发应用中发布-订阅模式的最终版代码
最近拜读了曾探所著的<JavaScript设计模式与开发应用>一书,在读到发布-订阅模式一章时,作者不仅给出了基本模式的通用版本的发布-订阅模式的代码,最后还做出了扩展,给该模式增加了离线 ...
- JS中的发布订阅模式
一. 你是如何理解发布订阅模式的 JS中的设计模式: 单例模式:处理业务逻辑 构造原型模式:封装类库,组件,框架,插件等 类库:jQuery 只是提供了一些常用的方法,可以应用到任何的项目中,不具备业 ...
- Javascript中理解发布--订阅模式
Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 如何实现发布--订阅模式? 发布---订阅模式的代码封装 如何取消订阅事件? 全局--发布订阅对象代码封装 理解模块间通信 回到 ...
- [转] Javascript中理解发布--订阅模式
发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 现实生活中的发布- ...
- JS中什么是发布--订阅模式?
转载文章部分内容: 发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. ...
- 【转】Javascript中理解发布--订阅模式
Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时 ...
- javascript中的发布订阅模式与观察者模式
这里了解一下JavaScript中的发布订阅模式和观察者模式,观察者模式是24种基础设计模式之一. 设计模式的背景 设计模式并非是软件开发的专业术语,实际上设计模式最早诞生于建筑学. 设计模式的定义是 ...
- javascript中的设计模式之发布-订阅模式
一.定义 又叫观察者模式,他定义对象间的依照那个一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将的到通知.在javascript中,我们一般用时间模型来替代传统的发布-订阅模式 二 ...
- 从发布订阅模式到redux(一)
最近在学习的过程中,学习了一些关于redux的一些知识,学之前用redux,虽然会用但是实现的原理就不是特别清楚,天天沉迷于搬砖 总是这木搬砖也不是个事啊,就准备开始深入了解一些原理性的东西 首先在看 ...
随机推荐
- 牛客网PAT-练兵场-挖掘机技术哪家强
题目地址:https://www.nowcoder.com/pat/6/problem/4058 题解:用数组下标当学校编号.输入一次数据的时候,直接在相应数组下标位置累加内容,同时更新最大的总分的学 ...
- 蒲公英 · JELLY技术周刊 Vol.19 从零开始的 Cloud IDE 开发
蒲公英 · JELLY技术周刊 Vol.19 你是否也会有想法去开发一个自己的 IDE 却苦于时间和精力不足,完成 Desktop IDE 却又被 Cloud IDE 的概念追在身后难以入睡,这样的两 ...
- 滴滴AI Labs斩获国际机器翻译大赛中译英方向世界第三
桔妹导读:深耕人工智能领域,致力于探索AI让出行更美好的滴滴AI Labs再次斩获国际大奖,这次获奖的项目是什么呢?一起来看看详细报道吧! 近日,由国际计算语言学协会ACL(The Associati ...
- Go语言从入门到高薪之路(一)-- 初识与安装
为什么要学习Go语言? Go语言又称Golang,它是21世纪的编程语言,学好了就能拿高薪,拿了高薪就能实力装逼,有了实力就能泡妹子...(我就问你吊不吊,想不想学?) Go语言有什么特点和优势? G ...
- 使用 Python破解大众点评字体加密(SVG反扒)
前言 大众点评拥有大量高质量评论信息.种草信息,同时也有非常严格的反扒机制. 今天我们一起使用 Python破解大众点评字体加密,获取极具商业价值的信息. 本文知识点: requests 的使用 xp ...
- 力扣Leetcode 1518. 换酒问题
小区便利店正在促销,用 numExchange 个空酒瓶可以兑换一瓶新酒.你购入了 numBottles 瓶酒. 如果喝掉了酒瓶中的酒,那么酒瓶就会变成空的. 请你计算 最多 能喝到多少瓶酒. 示例: ...
- Unity透明地形
http://answers.unity3d.com/questions/1162779/unity-5-transparent-terrain-shader.html http://answers. ...
- 《神经网络的梯度推导与代码验证》之vanilla RNN的前向传播和反向梯度推导
在本篇章,我们将专门针对vanilla RNN,也就是所谓的原始RNN这种网络结构进行前向传播介绍和反向梯度推导.更多相关内容请见<神经网络的梯度推导与代码验证>系列介绍. 注意: 本系列 ...
- Myabtis动态SQL,你真的会了吗?
目录 前言 什么是动态SQL? 常用的标签 if choose.when.otherwise where foreach set sql include 总结 拓展一下 Mybatis中如何避免魔数? ...
- 从String中移除空白字符的多种方式!?
字符串,是Java中最常用的一个数据类型了.我们在日常开发时候会经常使用字符串做很多的操作.比如字符串的拼接.截断.替换等. 这一篇文章,我们介绍一个比较常见又容易被忽略的一个操作,那就是移除字符串中 ...