Javascript 用于更新Redux状态的多个部分的模式

Javascript 用于更新Redux状态的多个部分的模式,javascript,redux,Javascript,Redux,我的Redux状态的形状如下所示: { user: { id: 123, items: [1, 2] }, items: { 1: { ... }, 2: { ... } } } 使用组合减速器,我有两套减速器。每个操作都作用于状态的一个根键。i、 e.一个管理用户键,另一个管理项键 如果我想添加一个可以调用2个reducer的项,第一个将向项添加一个新对象,第二个将向user.items数组添加id 这有

我的Redux状态的形状如下所示:

{
  user: {
    id: 123,
    items: [1, 2]
  },
  items: {
    1: {
      ...
    },
    2: {
      ...
    }
  }
}
使用组合减速器,我有两套减速器。每个操作都作用于状态的一个根键。i、 e.一个管理
用户
键,另一个管理

如果我想添加一个可以调用2个reducer的项,第一个将向
添加一个新对象,第二个将向
user.items
数组添加id


这有一种不好的代码味道。我觉得应该有一种方法可以在原子上同时减少两个对象的状态。i、 e.除子减速器外,还有一个作用于根对象的根减速器。这可能吗?

我认为你所做的实际上是正确的

在调度操作时,从根减速器开始,将调用每个“子减速器”,将相应的“子状态”和操作传递给下一层子减速器。您可能认为这不是一个好的模式,因为每个“子减速器”都会被调用并一直传播到状态树的每个叶节点,但事实并非如此

如果在开关案例中定义了该动作,“子减速器”将只更改其拥有的“子状态”部分,并可能将该动作传递到下一层,但如果“子减速器”中未定义该动作,则它将不执行任何操作并返回当前“子状态”,从而停止传播

让我们看一个更复杂的状态树示例


假设您使用
redux simple router
,我将您的情况扩展为更复杂(具有多个用户的数据),那么您的状态树可能如下所示:

{
  user: {
    id: 123,
    items: [1, 2]
  },
  items: {
    1: {
      ...
    },
    2: {
      ...
    }
  }
}
{
当前用户:{
洛格丁:没错,
id:123,
},
实体:{
用户:{
123: {
id:123,
项目:[1、2]
},
456: {
身份证号码:456,
项目:[……]
}
},
项目:{
1: {
...
},
2: {
...
}
}
},
路由:{
变更号:3,
路径:“/”,
状态:未定义,
替换:false
}
}
正如您已经看到的,在状态树中有嵌套的层,为了解决这个问题,我们使用了
reducer composition
,其概念是为状态树中的每个层使用
combineReducer()

因此,减速器应如下所示: (为了说明逐层的概念,这是由外向内的,因此顺序是向后的)

第一层:
从“redux简单路由器”导入{routeReducer}
函数currentUserReducer(状态={},操作){
开关(action.type){…}
}
const rootReducer=combinereducer({
currentUser:currentUserReducer,
实体:entitiesReducer,//来自第二层
路由:routeReducer//来自“redux简单路由器”
})
第二层(实体部分):
函数usersReducer(状态={},操作){
开关(动作类型){
案例添加项目:
案例类型二:
案例类型_树:
返回Object.assign({},state{
//您可以将其视为将其传递到“第三层”
[action.userId]:itemsusinerreducer(状态[action.userId],action)
})
案例类型四:
返回。。。
违约:
返回状态
}
}
函数项还原程序(…){…}
const entitiesReducer=组合减速机({
用户:usersReducer,
项目:项目还原器
})
第三层(实体、用户、项目):
/**
*注意:这里只调用ADD_ITEM,TYPE_TWO,TYPE_TREE,
*没有其他类型将传播到此减速器
*/
函数itemsusinerreducer(状态={},操作){
开关(动作类型){
案例添加项目:
返回Object.assign({},state{
items:state.items.concat([action.itemId])
//或项:[…state.items,action.itemId]
})
案例类型二:
回来做点什么
案例类型_树:
回来做点别的
违约:
声明:
}
}
当一个动作发出时 redux将从rootReducer调用每个子reducer,
通过:
currentUser:{…}
子状态和整个动作到
currentUserReducer

实体:{users:{…},items:{…}
和对
entitiesReducer的操作

路由:{…}
和对
路由转换器的操作

和…
entitiesReducer
用户:{…}
和操作传递给
用户reducer

items:{…}
和对
itemsReducer的操作

为什么这样好? 您提到过有一种方法可以让根减速器处理状态的不同部分,而不是将它们传递给单独的子减速器。但是,如果您不使用reducer合成并编写一个巨大的reducer来处理状态的每一部分,或者您只是将状态嵌套到一个深度嵌套的树中,那么随着应用程序变得更加复杂(比如每个用户都有一个
[friends]
数组,或者项目可以有
[tags]
,等等),如果不是不可能找出每一个案例,那将是极其复杂的

此外,拆分reducer使您的应用程序非常灵活,您只需将任何
案例类型\u NAME
添加到reducer即可对该操作作出反应(只要您的父reducer将其传递下去)


例如,如果要跟踪用户是否访问某条路线,只需将
案例更新路径添加到减速器开关中即可

当您解释操作会向下传播所有子约简时,我终于明白了,不管它们是否具有该操作。事后看来,这似乎很明显,但我终于看到了如何在Redux存储中处理关系数据的清晰模式。谢谢