[英]Am I mutating the redux state in reducer?
我正在尝试使用reducer更改我的状态中的数组中的一个项目。
州看起来像:
state: {
items: [
{
id: 1,
name: 'Superman',
wearsCape: true
},
{
id: 2,
name: 'Batman',
wearsCape: true
},
{
id: 3,
name: 'Iron Man',
wearsCape: false
}
];
}
我试图过滤州内的所有项目,以搜索名称等于sHero.name
的超级英雄的第一次sHero.name
。 然后我试图改变找到的超级英雄的属性。 reducer代码如下:
function findCapedCrusader(state, { sHero }) {
var result = state.items.filter(s => {
return s.name === sHero.name;
});
result[0].wearsCape = false;
return { ...state };
}
我是通过上述做法改变国家吗?
注意:为愚蠢的例子道歉。 我是新来的反应和减少。
filter()
方法返回一个新的数组实例,因此您不会改变您的状态。 有关更多信息,请参阅MDN文档 :
返回值
一个新数组,每个元素都是回调函数的结果。
是的,你在这个例子中改变你的状态。 虽然filter
确实产生了一个新的数组,但它实际上只是原始数据的浅层副本。 state.items
数组的元素是对象,因此当您在新results
数组(包含指向state.items
的原始内容的state.items
)中修改一个state.items
您将修改原始对象。 有关深度和浅拷贝的更多信息,请参阅此答案: 浅拷贝和带有JavaScript数组的深拷贝之间有什么区别?
要解决此问题,您可以:
1.在result
上使用spread运算符,然后覆盖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;
});
这种方法有点奇怪但适用于可序列化数据,如果要修改的值是深度嵌套的,并且多次使用扩展运算符( ...
)会变得很麻烦。
你是。 即使过滤器函数将生成一个新数组,状态也在您在该新数组中返回的对象中。 基于此,您正在改变状态,这不符合纯函数的含义。
在这里,我给你留下了一篇很好的文章,它将帮助你更好地理解深层文本是什么以及如何完成它。
https://medium.com/@tkssharma/objects-in-javascript-object-assign-deep-copy-64106c9aefab
据我所知, 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
。 如果您想用一个对象更新您的状态,我可以建议另一个解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.