当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《用于更新Redux状态的多个部分的模式》经验,为你挑选了1个好方法。

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

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

使用combineReducers我有2套减速器.每个行为都在州的一个根键上.即一个管理user密钥,另一个管理items密钥.

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

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



1> DaxChen..:

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

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

如果在切换情况下定义了操作,则"子减速器"将仅更改其拥有的"子状态"部分,并且可能将操作传递到下一层,但是如果操作未在" sub-reducer",它将什么都不做并返回当前的"子状态",这会停止传播.

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


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

{
  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() 在状态树的每一层.

所以你的reducer应该是这样的:(为了说明逐层概念,这是从外到内的,所以顺序是向后的)

第一层:

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'
})

第二层(实体部分):

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):

/**
 * 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:
  }
}

当一个动作发送时

终极版将调用每个子减速从rootReducer,
路过:
currentUser: {...}子状态和整个行动currentUserReducer
entities: {users: {...}, items: {...}}和行动entitiesReducer
routing: {...}和行动routeReducer
和......
entitiesReducer将通过users: {...}和行动usersReducer,
items: {...}和行动itemsReducer

为什么这样好?

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

此外,拆分缩减器使您的应用程序非常灵活,您只需添加任何case TYPE_NAME减速器以响应该操作(只要您的父减速器将其传递下去).

例如,如果您想跟踪用户是否访问某个路线,只需将其添加case UPDATE_PATH到您的减速器开关即可!

推荐阅读
sx-March23
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有