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();

这个事件系统具有注册,发布,移除事件的功能。那我们怎么在刚才这个场景去使用它尼?

  1. 发布:当A模块内数据因操作发生变化时,触发该数据变化的事件,定义typedata1Change
  2. 注册:这里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 中发布订阅模式使用的更多相关文章

  1. 理解JavaScript设计模式与开发应用中发布-订阅模式的最终版代码

    最近拜读了曾探所著的<JavaScript设计模式与开发应用>一书,在读到发布-订阅模式一章时,作者不仅给出了基本模式的通用版本的发布-订阅模式的代码,最后还做出了扩展,给该模式增加了离线 ...

  2. JS中的发布订阅模式

    一. 你是如何理解发布订阅模式的 JS中的设计模式: 单例模式:处理业务逻辑 构造原型模式:封装类库,组件,框架,插件等 类库:jQuery 只是提供了一些常用的方法,可以应用到任何的项目中,不具备业 ...

  3. Javascript中理解发布--订阅模式

    Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 如何实现发布--订阅模式? 发布---订阅模式的代码封装 如何取消订阅事件? 全局--发布订阅对象代码封装 理解模块间通信 回到 ...

  4. [转] Javascript中理解发布--订阅模式

    发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 现实生活中的发布- ...

  5. JS中什么是发布--订阅模式?

    转载文章部分内容: 发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. ...

  6. 【转】Javascript中理解发布--订阅模式

    Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时 ...

  7. javascript中的发布订阅模式与观察者模式

    这里了解一下JavaScript中的发布订阅模式和观察者模式,观察者模式是24种基础设计模式之一. 设计模式的背景 设计模式并非是软件开发的专业术语,实际上设计模式最早诞生于建筑学. 设计模式的定义是 ...

  8. javascript中的设计模式之发布-订阅模式

    一.定义 又叫观察者模式,他定义对象间的依照那个一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将的到通知.在javascript中,我们一般用时间模型来替代传统的发布-订阅模式 二 ...

  9. 从发布订阅模式到redux(一)

    最近在学习的过程中,学习了一些关于redux的一些知识,学之前用redux,虽然会用但是实现的原理就不是特别清楚,天天沉迷于搬砖 总是这木搬砖也不是个事啊,就准备开始深入了解一些原理性的东西 首先在看 ...

随机推荐

  1. 全局api 2020/8/14

    // 注意:每次调用 $.get() 或 $.post() 或 $.ajax() 的时候, // 会先调用 ajaxPrefilter 这个函数 // 在这个函数中,可以拿到我们给Ajax提供的配置对 ...

  2. TCP/IP网络编程之socket交互流程

    一.概要 本篇文章主要讲解基于.net中tcp/ip网络通信编程.在自我进步的过程中记录这些内容,方便自己记忆的同时也希望可以帮助到大家.技术的进步源自于分享和不断的自我突破. 技术交流QQ群:580 ...

  3. win环境下安装配置openCV-4.3.0

    win环境下安装openCV-4.3.0 首先下载 推荐国内镜像 官网太太太慢了 附上 下载地址 下载之后打开exe解压到目录都是常规操作 环境变量的配置 依次打开到系统变量的path 新建一个路径为 ...

  4. How to avoid multiple definition of function with gcc

    LDFLAGS add -Wl,--allow-multiple-definition

  5. js error 错误处理

    (new) Error([message[, fileName[,lineNumber]]]) 单独定义Error()错误,函数继续进行 当像函数一样使用 Error 时 -- 如果没有 new,它将 ...

  6. FIddlerd的下载教程和使用教程

    ------------恢复内容开始------------ .打开官网,官网下载地址是https://www.telerik.com/download/fiddler .打开以后选择你的相关信息如下 ...

  7. springboot x.x.x RELEASE不同版本的差异

    springboot 1.x.x RELEASE的 application.properties配置 server.context-path=/ server.port=8080 server.ses ...

  8. EMQ集群搭建实现高可用和负载均衡(百万级设备连接)

    一.EMQ集群搭建实现高可用和负载均衡 架构服务器规划 服务器IP 部署业务 作用 192.168.81.13 EMQTTD EMQ集群 192.168.81.22 EMQTTD EMQ集群 192. ...

  9. FFmpeg开发笔记(三):ffmpeg介绍、windows编译以及开发环境搭建

    前言   本篇章是对之前windows环境的补充,之前windows的是无需进行编译的,此篇使用源码进行编译,版本就使用3.4.8.   FFmpeg简介   FFmpeg是领先的多媒体框架,能够解码 ...

  10. html基础:基本标签

    一.html简介 html是一个长的字符串,它能够被浏览器解析.html分为三块:html代码,css,js. html的注释可以用<!-- --> 或者ctrl+? html页面打开以后 ...