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。
不过这也增加了几行的代码量,但比拟带来的上风来讲能够不计。
其他计划迎接人人批评
其他场景
待人人补充