Right way to clone objects / arrays during setState in React

前端 未结 4 1350
温柔的废话
温柔的废话 2021-02-20 10:36

I start with:

constructor() {
   super();
      this.state = {
         lists: [\'Dogs\',\'Cats\'], 
         items: {Dogs: [{name: \"Snoopy\"}, {name: \"Lola\"         


        
相关标签:
4条回答
  • 2021-02-20 11:08

    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.

    You could use Immutable.js to solve this issue.

    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!

    0 讨论(0)
  • 2021-02-20 11:08

    The chosen answer was a miracle and huge props to Nandu Kalidindi, I was having bugs with nested arrays inside of objects that I was updating in my project and was not understanding the concept of "deep" or "nested" arrays and objects not being copied to a new object.

    This is my take on his fix and made it look a little more appealing to me and works just as good!

    I utilize lodash and found their _.cloneDeep() function to serve my updating state needs.

    Lodash _.cloneDeep()

    I learned that mapping out updated arrays preventing any bugs was the best way to treat that kind of problem, but I was cloning entire objects with nested arrays that were mutating the old object's arrays in the state.

    This is my answer.

    
    const state = {
        fields: {
            "caNyDcSDVb": {
                id: "caNyDcSDVb",
                name: "test1",
                type: "text",
                options: ["A", "B", "C"]
            }
        },
    };
    
    const FieldCopy = (id) => {
        const newCopiedField = _.cloneDeep(state.fields[id]);
        newCopiedField.id = nanoid(10);
        return {
            ...state,
            fields: {
                ...state.fields,
                newCopiedField[id]: newCopiedField
            }
        };
    };
    
    0 讨论(0)
  • 2021-02-20 11:26

    I wanted to add a bit more info about cloning arrays. You can call slice, providing 0 as the first argument:

    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.

    0 讨论(0)
  • 2021-02-20 11:30

    I personally rely on this deep copy strategy. JSON.parse(JSON.stringify(object)) rather than spread operator because it got me into weird bugs while dealing with nested objects or multi dimensional arrays.

    spread operator does not do a deep copy if I am correct and will lead to state mutations with NESTED objects in React.

    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 spread operator.

    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});
    }
    
    0 讨论(0)
提交回复
热议问题