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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 07:30:42  来源:igfitidea点击:

Right way to clone objects / arrays during setState in React

javascriptreactjs

提问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.

上面的代码创建了原始数组的克隆;请记住,如果数组中存在对象,则保留引用;即上面的代码没有对数组内容进行“深度”克隆。