我正在尝试使用reducer在状态下更改数组中的一项。
状态如下:
state: {
items: [
{
id: 1,
name: 'Superman',
wearsCape: true
},
{
id: 2,
name: 'Batman',
wearsCape: true
},
{
id: 3,
name: 'Iron Man',
wearsCape: false
}
];
}
我正在尝试过滤状态中的所有项目,以搜索名称等于sHero.name
的超级英雄的首次出现。然后,我试图更改找到的超级英雄的属性。减速器代码如下:
function findCapedCrusader(state, { sHero }) {
var result = state.items.filter(s => {
return s.name === sHero.name;
});
result[0].wearsCape = false;
return { ...state };
}
我是通过上述操作来改变状态的吗?
注意:为愚蠢的例子道歉。我是新来的人,要做出反应并重新做准备。
答案 0 :(得分:0)
filter()
方法将返回一个新的数组实例,因此您不会改变状态。有关更多信息,请参见MDN documentation:
返回值
一个新数组,每个元素都是回调函数的结果。
答案 1 :(得分:0)
是的,在此示例中,您正在更改状态。尽管filter
确实产生了一个新数组,但实际上只是原始数组的浅表副本。 state.items
数组的元素是对象,因此,当您在新的results
数组(包含指向state.items
的原始内容的指针)中修改一个元素时,便会修改原始对象。有关深层副本和浅层副本的更多信息,请参见以下答案:What is the difference between a shallow copy and a deep copy with JavaScript arrays?
要解决此问题,您可以:
1。。在result
上使用扩展运算符,然后像这样覆盖wearsCape
的值:
result = state.items.filter(s => s.name === sHero.name).map(s => {
return {...s, wearsCape: false}
});
此方法适用于可序列化和不可序列化的数据,是更新嵌套状态值的推荐方法。
或
2。。在过滤前制作state.items
的深层副本:
const results = JSON.parse(JSON.stringify(state.items)).filter(s => {
return s.name === sHero.name;
});
这种方法有点奇怪,但是在要修改的值被深层嵌套并且多次使用扩展运算符(...
)的情况下,该方法非常适用于可序列化的数据。
答案 2 :(得分:0)
你是。即使filter函数将产生一个新数组,但状态仍在该新数组中返回的对象中。基于此,您正在更改状态,该状态不符合纯功能是什么。
在这里,我给您留下了一篇不错的文章,它将帮助您更好地理解什么是深层副本以及如何完成它。
https://medium.com/@tkssharma/objects-in-javascript-object-assign-deep-copy-64106c9aefab
答案 3 :(得分:0)
据我所知,filter
方法返回一个新数组,但是返回的元素仍然引用旧元素。因此,如果有人将某个属性突变为新创建的数组,那么他们也会对原始数组进行突变。
const state = {
items: [
{
id: 1,
name: 'Superman',
wearsCape: true
},
{
id: 2,
name: 'Batman',
wearsCape: true
},
{
id: 3,
name: 'Iron Man',
wearsCape: false
}
]
};
const newItem = state.items.filter( s => s.name === "Superman");
newItem[0].wearsCape = false;
console.log( "newitem", newItem, "\noriginal", state.items[0] );
如果要更改数组中一个对象的属性并更新状态:
const state = {
items: [
{
id: 1,
name: 'Superman',
wearsCape: true
},
{
id: 2,
name: 'Batman',
wearsCape: true
},
{
id: 3,
name: 'Iron Man',
wearsCape: false
}
]
};
const newItems = state.items.map( s => {
if( s.name === "Superman") {
return { ...s, wearsCape: false };
}
return s;
});
console.log( "new items", newItems, "original", state.items );
如您所见,原始items
没有被突变。对于您的情况,将是这样的:
function findCapedCrusader(state, { sHero }) {
var newItems = state.items.map(s => {
if (s.name === sHero.name ) {
return { ...s, wearsCape = false }
}
return s;
});
return { ...state, items: newItems };
}
同样,此函数将更改一个对象的属性,而不更改其他任何属性。由于您要在问题中尝试filter
,因此我不太确定这是您想要的。如果您想用一个对象更新状态,我可以建议另一种解决方案。