使用redux combineReducers减少整个子树

时间:2015-12-23 02:46:58

标签: reactjs flux redux

我有一个看起来像这样的reducer树:

module.exports = combineReducers({
    routing: routeReducer,
    app: combineReducers({
        setup: combineReducers({
            sets,
            boosters
        }),
        servers: combineReducers({
            servers
        })
    })
});

现在setup密钥包含一个表单,一旦我们提交它就需要重置。但是我无法访问整个setup树,因为使用combineReducers意味着reducers仅操作树的叶节点处的数据(在这种情况下为setsboosters)。 / p>

我的第一个冲动是创建一个减少整个设置树的函数,如下所示:

function setup(state, action){
    //If there's an action that affects this whole tree, handle it
    switch(action.type){
        case "FORM_SUBMIT": //DO STUFF
        break;
    }

    //Otherwise just let the reducers care about their own data
    return combineReducers({
                sets,
                boosters
    })(state);
}

但这不起作用,并且还弄乱了我的第一个代码示例的漂亮树结构。

使用redux有更好的解决方案吗?

2 个答案:

答案 0 :(得分:8)

combineReducers是一个很好的模式,因为它倾向于强制执行减速器应该限定为商店的非重叠子集的想法,与商店本身的结构分离。它认为你应该减少树叶而不是树枝,它会处理树枝的减少。

尽管如此,使用替代模式可能有充分的理由。正如我在稍微related question中提到的那样,您可以选择退出纯粹使用combineReducers并根据需要分解您的缩减器。

在您的情况下,您可以装饰内在的combineReducers

module.exports = combineReducers({
    routing: routeReducer,
    app: combineReducers({
        setup: setupReducer(
            combineReducers({
                sets,
                boosters
            })
        ),
        servers: combineReducers({
            servers
        })
    })
});

此处setupReducerhigher-order function。这可能很难理解,但这就是我如何处理它:

  • 我们知道setupReducer会将reducer作为参数,因为我们会将combineReducers的结果传递给它。
  • 我们知道combineReducers返回的reducer签名是(state, action) => state
  • 我们也知道setupReducer必须返回一个reducer,它同样是签名(state, action) => state的函数。

换句话说,它需要一个reducer,并返回一个reducer:((state, action) => state) => ((state, action) => state)。所以它可能看起来像:

function setupReducer(subReducer) {
    return (state, action) => {
        //If there's an action that affects this whole tree, handle it
        switch(action.type){
            case "FORM_SUBMIT": 
                // ... create newState
                return newState;
            default:
                return subReducer(state, action);
        }
    }
}

我保持你的逻辑流程在上面,但为了提醒你,你可能想要无条件地调用subReducer然后修改它的输出。否则,您必须确保未被调用的分支始终生成相同形状的对象,这似乎是耦合的潜在粘性点。

答案 1 :(得分:0)

@ acjay的回答是个好主意!我只是想使用旧方法reducer(state, action)而不是更高阶函数。所以我创建了一个以主从关系组合reducer的方法。

<强> MasterSlave

export default function masterSlaveReducer(reducerMaster, reducerSlave) {
    return (state, action) => {
        const newState = reducerMaster(state, action);
        if(newState === state){
            return reducerSlave(state, action);
        }

        return newState;
    };
}

Miguel的代码将以这种方式使用:

module.exports = combineReducers({
    routing: routeReducer,
    app: combineReducers({
        setup: masterSlaveReducer(
            setup, // setup() from Miguel's question
            combineReducers({
                sets,
                boosters
            })
        ),
        servers: combineReducers({
            servers
        })
    })
});