使用 redux combineReducers 减少整个子树
Reducing an entire subtree with redux combineReducers
我有一个看起来像这样的 reducer 树:
module.exports = combineReducers({
routing: routeReducer,
app: combineReducers({
setup: combineReducers({
sets,
boosters
}),
servers: combineReducers({
servers
})
})
});
现在 setup
键包含一个表单,一旦我们提交它就需要重新设置。但是我无法访问整个 setup
树,因为使用 combineReducers 意味着 reducer 仅在树的叶节点处操作数据(在本例中为 sets
和 boosters
)。
我的第一个冲动是制作一个像这样减少整个设置树的函数:
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 有更好的解决方案吗?
combineReducers
是一个很好的模式,因为它倾向于强制执行这样的想法,即 reducer 应该限定在 store 的非重叠子集中,与 store 本身的结构分离。它认为你应该减少树叶,而不是树枝,它处理树枝的减少。
也就是说,使用替代模式可能有充分的理由。正如我在稍微 中提到的,您可以选择不使用 combineReducers
并根据需要分解您的减速器。
在你的情况下,你可以装饰你的内心 combineReducers
:
module.exports = combineReducers({
routing: routeReducer,
app: combineReducers({
setup: setupReducer(
combineReducers({
sets,
boosters
})
),
servers: combineReducers({
servers
})
})
});
这里,setupReducer
是一个higher-order function。这可能很难推理,但我是这样处理的:
- 我们知道
setupReducer
将 reducer 作为参数,因为我们将 combineReducers
的结果传递给它。
- 我们知道由
combineReducers
编辑的reducerreturn的签名是(state, action) => state
。
- 我们还知道
setupReducer
必须 return 一个减速器,它又是签名的函数 (state, action) => state
.
换句话说,它需要一个减速器,return是一个减速器:((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
然后修改其输出。否则,您必须确保未调用的分支始终生成相同形状的对象,这似乎是耦合的潜在粘点。
@acjay 的回答是个好主意!我只是想使用旧方法 reducer(state, action)
而不是高阶函数。所以我创建了一个以主从关系组合reducer的方法。
主从
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
})
})
});
我有一个看起来像这样的 reducer 树:
module.exports = combineReducers({
routing: routeReducer,
app: combineReducers({
setup: combineReducers({
sets,
boosters
}),
servers: combineReducers({
servers
})
})
});
现在 setup
键包含一个表单,一旦我们提交它就需要重新设置。但是我无法访问整个 setup
树,因为使用 combineReducers 意味着 reducer 仅在树的叶节点处操作数据(在本例中为 sets
和 boosters
)。
我的第一个冲动是制作一个像这样减少整个设置树的函数:
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 有更好的解决方案吗?
combineReducers
是一个很好的模式,因为它倾向于强制执行这样的想法,即 reducer 应该限定在 store 的非重叠子集中,与 store 本身的结构分离。它认为你应该减少树叶,而不是树枝,它处理树枝的减少。
也就是说,使用替代模式可能有充分的理由。正如我在稍微 combineReducers
并根据需要分解您的减速器。
在你的情况下,你可以装饰你的内心 combineReducers
:
module.exports = combineReducers({
routing: routeReducer,
app: combineReducers({
setup: setupReducer(
combineReducers({
sets,
boosters
})
),
servers: combineReducers({
servers
})
})
});
这里,setupReducer
是一个higher-order function。这可能很难推理,但我是这样处理的:
- 我们知道
setupReducer
将 reducer 作为参数,因为我们将combineReducers
的结果传递给它。 - 我们知道由
combineReducers
编辑的reducerreturn的签名是(state, action) => state
。 - 我们还知道
setupReducer
必须 return 一个减速器,它又是签名的函数(state, action) => state
.
换句话说,它需要一个减速器,return是一个减速器:((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
然后修改其输出。否则,您必须确保未调用的分支始终生成相同形状的对象,这似乎是耦合的潜在粘点。
@acjay 的回答是个好主意!我只是想使用旧方法 reducer(state, action)
而不是高阶函数。所以我创建了一个以主从关系组合reducer的方法。
主从
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
})
})
});