javascript 在 React 中的 setState 期间克隆对象/数组的正确方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/47624142/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Right way to clone objects / arrays during setState in React
提问by DCR
I start with:
我开始:
constructor() {
super();
this.state = {
lists: ['Dogs','Cats'],
items: {Dogs: [{name: "Snoopy"}, {name: "Lola"}, {name: "Sprinkles"}],
Cats: [{name: "Felidae"}, {name: "Garfiled"}, {name: "Cat in the Hat"}] }
};
}
then I have my addItem function:
然后我有我的 addItem 功能:
handleAddItem(s) {
var key = Object.keys(s)[0];
var value = s[key];
var allItems = {...this.state.items};
allItems[key].push({name: value});
this.setState({items: allItems});
}
elsewhere I define s as:
在其他地方,我将 s 定义为:
var s={};
s[this.props.idName] = this.refs.id.value;
This works but I'm wondering if this is the right way to add an element into one of the keys in items. AllItems is really pointing to this.state.items and I thought it should be a deep copy but I'm not sure how to do that.
这有效,但我想知道这是否是将元素添加到 items 中的键之一的正确方法。AllItems 确实指向 this.state.items,我认为它应该是一个深层副本,但我不知道该怎么做。
It seems like items is an object that holds key/value pairs where the value is an array. Is that correct? Where can I go to learn how to manipulate a structure like that?
似乎 items 是一个保存键/值对的对象,其中值是一个数组。那是对的吗?我在哪里可以学习如何操作这样的结构?
回答by Nandu Kalidindi
I personally rely on this deep copy strategy.
JSON.parse(JSON.stringify(object))rather than spreadoperator because it got me into weird bugs while dealing with nested objects or multi dimensional arrays.
我个人依赖于这种深度复制策略。
JSON.parse(JSON.stringify(object))而不是spread运算符,因为它让我在处理嵌套对象或多维数组时遇到了奇怪的错误。
spreadoperator does not do a deep copy if I am correct and will lead to state mutations with NESTEDobjects in React.
spread如果我是正确的,操作符不会进行深度复制,并且会导致React 中NESTED对象的状态突变。
Please run through the code to get a better understanding of what is happening between the two. Imagine that is the state variable that you mutate using spreadoperator.
请运行代码以更好地了解两者之间发生的事情。想象一下,这是您使用spread运算符进行变异的状态变量。
const obj = {Dogs: [{name: "Snoopy"}, {name: "Lola"}, {name: "Sprinkles"}], Cats: [{name: "Felidae"}, {name: "Garfiled"}, {name: "Cat in the Hat"}] };
const newObj = {...obj};
console.log("BEFORE SPREAD COPY MUTATION")
console.log("NEW OBJ: " + newObj.Dogs[0].name); //Snoopy
console.log("OLD OBJ: " + obj.Dogs[0].name); //Snoopy
newObj.Dogs[0].name = "CLONED Snoopy";
console.log("AFTER SPREAD COPY MUTATION")
console.log("NEW OBJ: " + newObj.Dogs[0].name); // CLONED Snoopy
console.log("OLD OBJ: " + obj.Dogs[0].name); // CLONED Snoopy
// Even after using the spread operator the changed on the cloned object are affected to the old object. This happens always in cases of nested objects.
// My personal reliable deep copy
console.log("*********DEEP COPY***********");
console.log("BEFORE DEEP COPY MUTATION")
deepCopyObj = JSON.parse(JSON.stringify(obj));
console.log("NEW OBJ: " + newObj.Dogs[0].name); //CLONED Snoopy
console.log("OLD OBJ: " + obj.Dogs[0].name); // CLONED Snoopy
console.log("DEEP OBJ: " + deepCopyObj.Dogs[0].name); //CLONED Snoopy
deepCopyObj.Dogs[0].name = "DEEP CLONED Snoopy";
console.log("AFTER DEEP COPY MUTATION")
console.log("NEW OBJ: " + newObj.Dogs[0].name); // CLONED Snoopy
console.log("OLD OBJ: " + obj.Dogs[0].name); // CLONED Snoopy
console.log("DEEP OBJ: " + deepCopyObj.Dogs[0].name); // DEEP CLONED Snoopy
Now, if you wanted to do a deep copy on your object change the handler to this
现在,如果您想对您的对象进行深度复制,请将处理程序更改为此
handleAddItem(s) {
var key = Object.keys(s)[0];
var value = s[key];
var allItems = JSON.parse(JSON.stringify(this.state.items));
allItems[key].push({name: value});
this.setState({items: allItems});
}
回答by willwoo
One issue might be that var allItems = {...this.state.items};will only do a shallow clone of this.state.items. So when you push data into this array, it will change the existing array before you call setState.
一个问题可能是var allItems = {...this.state.items};它只会对this.state.items. 所以当你将数据推入这个数组时,它会在你调用之前改变现有的数组setState。
You could use Immutable.jsto solve this issue.
你可以使用Immutable.js来解决这个问题。
import { List, fromJS, Map } from 'immutable';
constructor() {
super();
this.state = {
lists: List(['Dogs','Cats']),
items: fromJS({
Dogs: [
{ name: "Snoopy" },
...
],
Cats: [
{ name: "Felidae" },
...
]
})
};
}
and then your add function would be as follow:
然后你的添加功能如下:
handleAddItem(s) {
var key = Object.keys(s)[0];
var value = s[key];
var allItems = this.state.items.set(key, Map({ name: value }));
this.setState({ items: allItems });
}
Just a thought!
只是一个想法!
回答by mrded
I wanted to add a bit more info about cloning arrays. You can call slice, providing 0 as the first argument:
我想添加更多有关克隆数组的信息。您可以调用slice,提供 0 作为第一个参数:
const clone = myArray.slice(0);
The code above creates clone of the original array; keep in mind that if objects exist in your array, the references are kept; i.e. the code above does not do a "deep" clone of the array contents.
上面的代码创建了原始数组的克隆;请记住,如果数组中存在对象,则保留引用;即上面的代码没有对数组内容进行“深度”克隆。

