如何使用嵌套对象编写 state reducer

how to write state reducer with nested objects

我的 redux 状态树心智模型看起来像这样:

{
  selectedDepartment: 'Chemistry',
  purchasesByDepartment: {
    Chemistry: {
      purchaseOrders: {
        ...
      },
      invoices: {
        ...
      }
    },
    Biology: {
      purchaseOrders: {
        ...
      },
      invoices: {
        ...
      }
    },

    ...(another department, etc)
  }
}

这是我的减速器

const purchasesByDepartment = (state = {}, action) => {
  switch (action.type) {
    case RECEIVE_POS:
      return Object.assign({}, state, {
        [action.department]: {
          purchaseOrders: action.json
          // but this wipes out my invoices          
        }
      })
    case RECEIVE_INVOICES:
      return Object.assign({}, state, {
        [action.department]: {          
          invoices: action.json
          // but this wipes out my purchaseOrders
        }
      })

    default:
      return state
  }
}

你可以看到在每个部门,我有一个 purchaseOrders 和一个 invoices 键。 我正在尽最大努力编写我的减速器,使其不会改变我的状态,但我并不走运,因为目前每个动作都会清除另一个键。

我启动了两个操作:RECEIEVE_POS、RECEIVE_INVOICES。

当我的操作 RECEIVE_POS 被调度时,我可以创建一个新的采购订单状态,但这会清除我的发票。

当我的操作 RECEIVE_INVOICES 被发送时,我可以创建一个新的发票状态,但这会清除我的采购订单。

如何编写我的 reducer 以便我可以保留我的采购订单和发票(如果它们已经存在于我的州)?

您可以先通过应用更改为部门创建新状态,然后将其合并到 reducer 状态。

const purchasesByDepartment = (state = {}, action) => {
  switch (action.type) {
    case RECEIVE_POS:
      let nextPOS = Object.assign({}, state[action.department] || {}, {
        purchaseOrders: action.json
      });

      return Object.assign({}, state, nextPOS);
    default:
      return state
  }
}

就我个人而言,我喜欢通过使用 immutablejs 让我的整个商店不可变,这使得像这样的操作(以及其他一些事情)变得微不足道:

return state.setIn([action.department, 'purchaseOrders'], action.json]);

我使用扩展运算符(表示为...arrayName)来实现这一点。是 es6,我用 babel 启用了功能。

Spread 会将数组展开到其元素中,因为它比 Object.assign 更具人类可读性。

使用你的上下文,对于我的减速器中的一个简单场景,我只是喜欢:

return {
    ...state,
    selectedDepartment: 'Chemistry or whatever' 
}

这将return一个由状态组成的数组,加上用给定值覆盖的selectedDepartment。

对于嵌入式对象你可以这样写:

return {
    ...state,
    purchasesByDepartment: {
        ...state.purchasesByDepartment,
        Chemistry: {
            ...state.purchasesByDepartment.Chemistry,
            invoices: {
                {your new invoices object comes here}
            }
        }
    }
}

对象传播结合计算的属性名称帮助我解决了这个问题

case RECEIVE_POS:
  return {
    ...state,
    [action.department]: {
      ...state[action.department],
      purchaseOrders: action.json
    }
  }
case RECEIVE_INVOICES:
  return {
    ...state,
    [action.department]: {
      ...state[action.department],
      invoices: action.json
    }
  }