我有一个看起来像这样的reducer树:
module.exports = combineReducers({ routing: routeReducer, app: combineReducers({ setup: combineReducers({ sets, boosters }), servers: combineReducers({ servers }) }) });
现在,setup
密钥包含一个表单,一旦我们提交它就需要重置.不过,我也没有办法访问整个setup
树,因为使用combineReducers意味着减速器只在树(的叶节点操纵数据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
是一个很好的模式,因为它倾向于强制执行减速器应限定为商店的非重叠子集的想法,与商店本身的结构分离.它认为你应该减少叶子,而不是分支,它处理分支的减少.
也就是说,使用替代模式可能有充分的理由.正如我在一个稍微相关的问题中所提到的,您可以选择不使用纯粹使用combineReducers
和分解您的减速器.
在你的情况下,你可以装饰你的内心combineReducers
:
module.exports = combineReducers({ routing: routeReducer, app: combineReducers({ setup: setupReducer( combineReducers({ sets, boosters }) ), servers: combineReducers({ servers }) }) });
这里setupReducer
是一个高阶函数.这可能很难理解,但这是我如何处理它:
我们知道setupReducer
将reducer作为参数,因为我们将结果传递combineReducers
给它.
我们知道返回的减速器的签名combineReducers
是(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
无条件地调用然后修改它的输出.否则你必须确保你未被调用的分支总是产生一个相同形状的对象,这似乎是一个潜在的耦合点.