用于更新Redux状态的多个部分的模式 [英] Pattern for updating multiple parts of Redux state

查看:93
本文介绍了用于更新Redux状态的多个部分的模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The shape of my Redux state looks like this:

{
  user: {
    id: 123,
    items: [1, 2]
  },
  items: {
    1: {
      ...
    },
    2: {
      ...
    }
  }
}

使用combineReducers我有2套减速器。每个行为都在州的一个根键上。即一个管理用户键,另一个管理键。

Using combineReducers I have 2 sets of reducers. Each act on one of the root keys of the state. i.e. one manages the user key and the other the items key.

如果我想添加一个项目,我可以调用2个reducers,第一个将新的对象添加到 items ,第二个将id添加到 user.items array。

If I want to add an item I can call 2 reducers, the first will add a new object to the items and the second will add the id to the user.items array.

这有一个糟糕的代码味道。我觉得应该有一种方法可以同时原子地减少两个对象的状态。即,除了子减速器之外还有一个根减速器作用于根对象。这可能吗?

This has a bad code smell. I feel that there should be a way to atomically reduce the state of both objects at the same time. i.e. in addition to the sub-reducers have a root reducer that acts on the root object. Is this possible?

推荐答案

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

I think what you're doing is actually correct!

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

When dispatching an action, starting from the root-reducer, every "sub-reducer" will be called, passing the corresponding "sub-state" and action to the next layer of sub-reducers. You might think that this is not a good pattern since every "sub-reducer" gets called and propagates all the way down to every single leaf node of the state tree, but this is actually not the case!

如果在开关案例中定义了动作,sub-reducer将只改变它所拥有的子状态部分,并且可能将动作传递给下一层,但是如果动作没有在减速器中定义,它什么都不做,并返回当前的子状态,这会停止传播。

If the action is defined in the switch case, the "sub-reducer" will only change the "sub-state" part it owns, and maybe passes the action to the next layer, but if the action isn't defined in the "sub-reducer", it will do nothing and return the current "sub-state", which stops the propagation.

让我们看看使用更复杂的状态树的示例!

Let's see an example with a more complex state tree!

假设您使用 redux-simple-router ,我将你的案例扩展为更复杂(拥有多个用户的数据),然后你的州树可能看起来像这样:

Say you use redux-simple-router, and I extended your case to be more complex (having data of multiple users), then your state tree might look something like this:

{
  currentUser: {
    loggedIn: true,
    id: 123,
  },
  entities: {
    users: {
      123: {
        id: 123,
        items: [1, 2]
      },
      456: {
        id: 456,
        items: [...]
      }
    },
    items: {
      1: {
        ...
      },
      2: {
        ...
      }
    }
  },
  routing: {
    changeId: 3,
    path: "/",
    state: undefined,
    replace:false
  }
}

正如您所看到的,状态树中有 嵌套 图层,为了解决这个问题,我们使用 reducer composition ,概念是使用 combineReducer() 状态树中的每一层。

As you can see already, there are nested layers in the state tree, and to deal with this we use reducer composition, and the concept is to use combineReducer() for every layer in the state tree.

所以你的reducer应该是这样的:
(为了说明逐层概念,这是从外到底,所以订单是倒退的)

So your reducer should look something like this: (To illustrate the layer by layer concept, this is outside-in, so the order is backwards)

import { routeReducer } from 'redux-simple-router'

function currentUserReducer(state = {}, action) {
  switch (action.type) {...}
}

const rootReducer = combineReducers({
  currentUser: currentUserReducer,
  entities: entitiesReducer, // from the second layer
  routing: routeReducer      // from 'redux-simple-router'
})



第二层(实体部分):



second layer (the entities part):

function usersReducer(state = {}, action) {
  switch (action.type) {
  case ADD_ITEM:
  case TYPE_TWO:
  case TYPE_TREE:
    return Object.assign({}, state, {
      // you can think of this as passing it to the "third layer"
      [action.userId]: itemsInUserReducer(state[action.userId], action)
    })
  case TYPE_FOUR:
    return ...
  default:
    return state
  }
}

function itemsReducer(...) {...}

const entitiesReducer = combineReducers({
  users: usersReducer,
  items: itemsReducer
})



第三层(entities.users.items):



third layer (entities.users.items):

/**
 * Note: only ADD_ITEM, TYPE_TWO, TYPE_TREE will be called here,
 *       no other types will propagate to this reducer
 */
function itemsInUserReducer(state = {}, action) {
  switch (action.type) {
  case ADD_ITEM:
    return Object.assign({}, state, {
      items: state.items.concat([action.itemId])
      // or items: [...state.items, action.itemId]
    })
  case TYPE_TWO:
    return DO_SOMETHING
  case TYPE_TREE:
    return DO_SOMETHING_ELSE
  default:
    state:
  }
}



当一个动作发送



redux将从rootReducer调用每个子减速器,

传递:

currentUser:{...} sub-stat e和整个行动 currentUserReducer

entities:{users:{...},items:{... } 和行动到 entitiesReducer

路由:{...} 和行动到 routeReducer

和...

entitiesReducer 将传递用户:{...} 并将操作传递给 usersReducer

项目:{...} 并且行动到 itemsReducer

when an action dispatches

redux will call every sub-reducer from the rootReducer,
passing:
currentUser: {...} sub-state and the whole action to currentUserReducer
entities: {users: {...}, items: {...}} and action to entitiesReducer
routing: {...} and action to routeReducer
and...
entitiesReducer will pass users: {...} and action to usersReducer,
and items: {...} and action to itemsReducer

所以你提到有一种方法让根减速器处理状态的不同部分,而不是将它们传递给单独的子减速器。但是如果你不使用reducer组合并编写一个巨大的reducer来处理状态的每个部分,或者你只是将状态嵌套到一个深度嵌套的树中,那么当你的app变得更复杂时(比如每个用户都有一个<$ c) $ c> [朋友] 数组,或者项目可以有 [tags] 等等,如果不是不可能搞清楚,那将是非常复杂的每个案例。

So you mentioned is there a way to have the root reducer handling different parts of the state, instead of passing them to separate sub-reducers. But if you don't use reducer composition and write a huge reducer to handle every part of the state, or you simply nest you state into a deeply nested tree, then as your app gets more complicated (say every user has a [friends] array, or items can have [tags], etc), it will be insanely complicated if not impossible to figure out every case.

此外,拆分缩减器使您的应用非常灵活,您只需添加任何案例TYPE_NAME 即可减速器对该动作做出反应(只要你的父减速器将其传递下去)。

Furthermore, splitting reducers makes your app extremely flexible, you just have to add any case TYPE_NAME to a reducer to react to that action (as long as your parent reducer passes it down).

例如,如果你想跟踪用户是否访问某条路线,只需添加你的减速机开关的案例UPDATE_PATH

For example if you want to track if the user visits some route, just add the case UPDATE_PATH to your reducer switch!

这篇关于用于更新Redux状态的多个部分的模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆