combineReducers 进阶之不同 reducers 之间共享数据

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/arvin_top/article/details/85085232

如果 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 树已经存在,也就不会为此提供默认状态。

猜你喜欢

转载自blog.csdn.net/arvin_top/article/details/85085232