Redux reducer - 改变深层嵌套状态

时间:2016-03-11 18:32:02

标签: javascript reactjs redux

我正试图围绕如何在redux中更改深层嵌套状态。对我来说,组合减速器并改变这些部件的状态的第一级属性是有意义的。我不太清楚的是如何改变深层嵌套属性的状态。 让我们假装我有一个购物车应用程序。以下是我的状态:

{
    cart: {
      items: []
    },
    account: {
      amountLeft: 100,
      discounts: {
        redeemed: [],
        coupons: {
          buyOneGetOne: false
        }
      }
    }  
}

当用户输入代码时,假设他们可以兑换“buyOneGetOne”优惠券,该值应该成立。 我有一个推车减速器和另一个帐户。对于一级房产(如果我清理了购物车的物品),我会在减速机中执行以下操作:

case 'EMPTY_CART':
  return Object.assign({}, state, {items: []});

然而,为了改变buyOneGetOne,似乎我首先需要在优惠券上做一个Object.assign(因为buyOneGetOne被修改),然后在折扣上做一个Object.assign(因为我修改过优惠券),然后最后发出动作使减速器可以对帐户进行Object.assign(因为折扣现在已经改变)。然而,这似乎非常复杂且容易出错,这使我相信必须有更好的方法。

我是否认为这一切都错了?似乎reducer仅用于修改状态的根级属性(如购物车和帐户),并且我不应该有一个涉及帐户内部状态的减速器(如折扣减少器),因为帐户已经有减速器。但是当我只想在状态树下更改一个属性时,将每个对象从对象链中的每个对象合并到根的子节点都变得很复杂......

你能/你应该在减速器里面有减速器吗,比如在这种情况下有减速器吗?

3 个答案:

答案 0 :(得分:31)

你绝对可以在减速器内部使用减速器;事实上,redux演示应用程序执行此操作:http://redux.js.org/docs/basics/Reducers.html

例如,您可以创建一个名为`discounts'的嵌套reducer:

function discounts(state, action) {
    switch (action.type) {
        case ADD_DISCOUNT:
            return state.concat([action.payload]);

        // etc etc
    }
}

然后在帐户缩减器中使用此reducer:

function account(state, action) {
    switch (action.type) {
        case ADD_DISCOUNT:
            return {
                ...state,
                discounts: discounts(state.discounts, action)
            };

         // etc etc
    }
}

要了解详情,请查看Dan本人的egghead.io redux series,特别是reducer composition视频!

答案 1 :(得分:4)

你应该为每个级别

嵌套combinedReducers

答案 2 :(得分:0)

是的,将这些减速器分开是正确的。实际上,如果你害怕其中一些动作需要改变其他减速器,你可以对其他常数做出反应,或者只是发出另一个动作。

I've created a library解决此问题 - 允许简单的合成,并避免冗长的语法,以及合并结果。 所以,你的例子看起来像这样:

import { createTile, createSyncTile } from 'redux-tiles';

const cartItems = createSyncTile({
  type: ['account', 'cart'],
  fn: ({ params }) => ({ items: params.items })
});

const coupons = createSyncTile({
  type: ['account', 'coupons'],
  fn: ({ params }) => params,
});

const reedemedCoupons = createSyncTile({
  type: ['account', 'reedemedCoupons'],
  fn: ({ params }) => params.coupons
});

const account = createTile({
  type: ['account', 'info'],
  fn: ({ api }) => api.get('/client/info')
});

使用此策略,每个组件都是原子的,您可以轻松地创建新组件并调度其他组件,而不会影响其他组件,这样可以在以后更轻松地重构和删除某些功能,当您的" tile"遵循单一责任原则。