怎麼能將設計模式應用到咱們的 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。
不過這也增長了幾行的代碼量,但相比帶來的優點來講能夠不計。
其餘方案歡迎你們評論
待你們補充