combineReducers 進階之不同 reducers 之間共享資料
阿新 • • 發佈:2019-01-10
如果 sliceReducerA
為了處理特殊的 action 正好需要來自 sliceReducerB
的部分 state 資料,或者 sliceReducerB
正好需要全部的 state 作為引數,單單就 combineReducers
是無法解決這種問題的。可以這樣來解決:把所需資料當額外引數的形式傳遞給自定義函式,例如:
function combinedReducer(state, action) { switch(action.type) { case "A_TYPICAL_ACTION" : { return { a : sliceReducerA(state.a, action), b : sliceReducerB(state.b, action) }; } case "SOME_SPECIAL_ACTION" : { return { // 明確地把 state.b 作為額外引數進行傳遞 a : sliceReducerA(state.a, action, state.b), b : sliceReducerB(state.b, action) } } case "ANOTHER_SPECIAL_ACTION" : { return { a : sliceReducerA(state.a, action), // 明確地把全部的 state 作為額外引數進行傳遞 b : sliceReducerB(state.b, action, state) } } default: return state; } }
另一種解決“共享片段資料更新” (shared-slice updates) 問題的簡單方法是,給 action 新增額外資料。可以通過 thunk 函式或者類似的方法輕鬆實現,如下:
function someSpecialActionCreator() { return (dispatch, getState) => { const state = getState(); const dataFromB = selectImportantDataFromB(state); dispatch({ type : "SOME_SPECIAL_ACTION", payload : { dataFromB } }); } }
因為 B 的資料已經存在於 action 中,所以它的父級 reducer 不需要做任何特殊的處理就能把資料暴露給 sliceReducerA
。
第三種方法是:使用 combineReducers
組合 reducer 來處理“簡單”的場景,每個 reducer 依舊只更新自己的資料;同時新加一個 reducer 來處理多塊資料交叉的“複雜”場景;最後寫一個包裹函式依次呼叫這兩類 reducer 並得到最終結果:
const combinedReducer = combineReducers({ a : sliceReducerA, b : sliceReducerB }); function crossSliceReducer(state, action) { switch(action.type) { case "SOME_SPECIAL_ACTION" : { return { // 明確地把 state.b 作為額外引數進行傳遞 a : handleSpecialCaseForA(state.a, action, state.b), b : sliceReducerB(state.b, action) } } default : return state; } } function rootReducer(state, action) { const intermediateState = combinedReducer(state, action); const finalState = crossSliceReducer(intermediateState, action); return finalState; }
已經有一個庫 reduce-reducers 可以簡化以上操作流程。它接收多個 reducer 然後對它們依次執行 reduce()
操作,並把產生的中間值依次傳遞給下一個 reducer:
// 與上述手動編寫的 `rootReducer` 一樣
const rootReducer = reduceReducers(combinedReducers, crossSliceReducer);
值得注意的是,如果你使用 reduceReducers
你應該確保第一個 reducer 能夠定義初始狀態的 state 資料,因為後續的 reducers 通常會假定 state 樹已經存在,也就不會為此提供預設狀態。