Redux 扩展运算符与 Map

IT技术 javascript reactjs dictionary react-redux spread
2021-05-24 17:42:32

我有一个数组中的对象状态(在我的 Redux Reducer 中)。

const initialState = {
      items: [
        { id: 1, dish: "General Chicken", price: 12.1, quantity: 0 },
       { id: 2, dish: "Chicken & Broccoli", price: 10.76, quantity: 0 },
        { id: 3, dish: "Mandaran Combination", price: 15.25, quantity: 0 },
        { id: 4, dish: "Szechuan Chicken", price: 9.5, quantity: 0 }
      ],
      addedItems: [],
     total: 0
    };

当在 Cart.jsx 中单击按钮时,我有一个将对象的数量加 1 的操作,例如 {id:1,dish:Generals Chicken, price:10.76,quantity:0}。这是我尝试使用扩展运算符的第一个 Reducer:

case "ADD_QUANTITY":
  let existing_item = state.addedItems.find(
    item => action.payload === item.id
  );

  return {
    ...state,
    addedItems: [
      ...state.addedItems,
      { ...existing_item, quantity: existing_item.quantity + 1 }
    ]
  };

这不起作用,它没有将数量添加到 1,而是添加了另一个将数量设置为 2 的对象。.所以,我尝试像这样使用 Map

case "ADD_QUANTITY":
  return {
    ...state,
    addedItems: state.addedItems.map(item =>
      item.id === action.payload
        ? { ...item, quantity: item.quantity + 1 }
        : item
    )
  };

这工作正常。我的问题是,为什么点差运算符不起作用?据我所知,它应该和 Map 做同样的事情?

3个回答

这两段代码完全不同。

第一个从state.addedItems和 新对象创建一个新数组{ ...existing_item, quantity: existing_item.quantity + 1 },然后将该数组分配给addedItems状态属性。

第二段代码进行迭代addedItems,如果它找到一个与有效负载具有相同 id 的元素,它会创建一个新对象{ ...item, quantity: item.quantity + 1 }并返回该对象,而不是数组中的原始项目。

因此,即使两种方法都创建了一个新数组,第一种方法与原始数组相比有一个额外的对象,而第二种方法有一个具有修改属性的对象。

当在数组文字上下文中使用时,展开语法不会重现键(索引),而只会重现值。与生成键/值对的对象字面量上下文中的展开语法相反。后者允许以前的条目被具有相同键的新条目否决,但第一个没有这种行为:它总是传播所有值而不考虑索引。

替换数组中的元素时,复制时需要:

  • 知道应该执行替换的索引,以及
  • 确保副本是 Array 的实例,而不仅仅是普通对象

您可以使用findIndexObject.assign([], )解决这些需求:

case "ADD_QUANTITY":
  let index = state.addedItems.findIndex(
    item => action.payload === item.id
  );
  existing_item = state.addedItems[index];

  return {
    ...state,
    addedItems: Object.assign([], state.addedItems, {
      [index]: { ...existing_item, quantity: existing_item.quantity + 1 }
    })
  }

这是因为在您的传播示例中,它无法确定应该覆盖哪个对象。因此,它的操作与您可能会看到的其他一些示例并不完全相同。考虑以下:

如果你有一个这样的对象:

let test = { a: 'first', b: 'second' }

然后像这样传播会起作用:

let newTest = {...test, b: 'third' }

原因是您指定b应该被覆盖。

当您尝试使用对象数组创建相同的效果时,您无法指定键。所以你实际上做的只是将新对象附加到数组的末尾。

在地图示例中,您正在检查对象内容并根据它是否符合您的条件返回不同的对象,因此您知道要覆盖哪个对象。