Redux Spread 运算符与 Map
Redux Spread Operator vs Map
我有一个数组中的对象状态(在我的 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
};
我有一个动作是将一个对象的数量加1,比如
{id:1, 菜: 将军鸡, 价格: 10.76, quantity:0}
当在 Cart.jsx 中单击按钮时。这是我尝试使用展开运算符的第一个 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 做同样的事情?
这两段代码有很大的不同。
第一个从 state.addedItems
和新对象 { ...existing_item, quantity: existing_item.quantity + 1 }
中创建一个新数组,然后将该数组分配给状态的 addedItems
属性。
第二段代码迭代addedItems
,如果它找到一个与有效载荷具有相同id的元素,它会创建一个新对象{ ...item, quantity: item.quantity + 1 }
和returns那个,而不是数组中的原始项目。
因此,尽管这两种方法都创建了一个新数组,但第一种方法与原始数组相比有一个额外的对象,而第二种方法有一个修改过的对象 属性。
这是因为在您的传播示例中,它无法判断应该覆盖哪个对象。所以它的操作与您可能看到的其他一些示例并不完全相同。考虑以下因素:
如果你有这样的对象:
let test = { a: 'first', b: 'second' }
然后像这样传播会起作用:
let newTest = {...test, b: 'third' }
原因是您指定 b
应该被覆盖。
当您尝试使用一组对象创建相同的效果时,您无法指定键。所以你实际做的只是将新对象附加到数组的末尾。
在地图示例中,您正在检查对象内容并根据是否符合您的条件返回不同的对象,因此您知道要覆盖哪个对象。
传播语法在数组文字上下文中使用时,不会重现键(索引),而只会重现值。与生成 key/value 对的对象文字上下文中的扩展语法相反。后者允许以前的条目被具有相同键的新条目否决,但第一个条目没有这种行为:它总是传播所有值而不考虑索引。
替换数组中的元素时,在复制它的同时,您需要:
- 知道应该执行替换的索引,并且
- 确保副本是 Array 的一个实例,而不仅仅是一个普通对象
您可以使用 findIndex
和 Object.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 }
})
}
我有一个数组中的对象状态(在我的 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
};
我有一个动作是将一个对象的数量加1,比如 {id:1, 菜: 将军鸡, 价格: 10.76, quantity:0} 当在 Cart.jsx 中单击按钮时。这是我尝试使用展开运算符的第一个 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 做同样的事情?
这两段代码有很大的不同。
第一个从 state.addedItems
和新对象 { ...existing_item, quantity: existing_item.quantity + 1 }
中创建一个新数组,然后将该数组分配给状态的 addedItems
属性。
第二段代码迭代addedItems
,如果它找到一个与有效载荷具有相同id的元素,它会创建一个新对象{ ...item, quantity: item.quantity + 1 }
和returns那个,而不是数组中的原始项目。
因此,尽管这两种方法都创建了一个新数组,但第一种方法与原始数组相比有一个额外的对象,而第二种方法有一个修改过的对象 属性。
这是因为在您的传播示例中,它无法判断应该覆盖哪个对象。所以它的操作与您可能看到的其他一些示例并不完全相同。考虑以下因素:
如果你有这样的对象:
let test = { a: 'first', b: 'second' }
然后像这样传播会起作用:
let newTest = {...test, b: 'third' }
原因是您指定 b
应该被覆盖。
当您尝试使用一组对象创建相同的效果时,您无法指定键。所以你实际做的只是将新对象附加到数组的末尾。
在地图示例中,您正在检查对象内容并根据是否符合您的条件返回不同的对象,因此您知道要覆盖哪个对象。
传播语法在数组文字上下文中使用时,不会重现键(索引),而只会重现值。与生成 key/value 对的对象文字上下文中的扩展语法相反。后者允许以前的条目被具有相同键的新条目否决,但第一个条目没有这种行为:它总是传播所有值而不考虑索引。
替换数组中的元素时,在复制它的同时,您需要:
- 知道应该执行替换的索引,并且
- 确保副本是 Array 的一个实例,而不仅仅是一个普通对象
您可以使用 findIndex
和 Object.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 }
})
}