1. 程式人生 > >combineReducers 進階之不同 reducers 之間共享資料

combineReducers 進階之不同 reducers 之間共享資料

如果 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 樹已經存在,也就不會為此提供預設狀態。