怎么能将设计模式应用到咱们的 React 项目中?之前一直在思考这个问题。javascript
模块 A 模块 B 须要用到同一个数据 data,A 和 B 都会修改这份数据,且这两个模块会同时存在;这时咱们如何作到数据公用与各个模块的更新?java
方案一:
将这份数据做为公共的数据 data,A B 模块同时使用并更改这份数据这一份数据。若使用 redux 代码多是这样:react
// 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 完美解决redux
方案二:待补充后端
A 模块使用了 data1, B 模块使用了 data2;A B 模块能够修改对应的 data;这两份 data 结构上不一样,可是存在业务上的联系: 当 data1 更新后须要 data2 更新;data2 更新一样须要 data1 同步;对应后端的两个不一样的 API。设计模式
咱们整理一下async
当其中一个数据因操做发生更新时,判断另外一个模块是否存在 若是存在则调用他的数据更新逻辑;this
若是你使用了 redux,可能方便一点:spa
// 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();
这个事件系统具备注册,发布,移除事件的功能。那咱们怎么在刚才这个场景去使用它尼?
type
为data1Change
;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。
不过这也增长了几行的代码量,但相比带来的优点来讲能够不计。
其余方案欢迎你们评论
待你们补充