Right way to clone objects / arrays during setState in React

react update object in array
react setstate array not working
react setstate nested array
react state array of objects
react array push
react array of objects
react native array of objects
react state object

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:

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:

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.

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?

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

Right way to clone objects / arrays during setState in React, 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​  Clone the current state using slice(). By doing this, the original state remains unaffected till setState(). After cloning, do your operations over the cloned array and set it in the state. The previous answer will mutate the state.

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!

.setState() Not Working? Shallow Copy vs Deep Copy & Lodash, There are times in React and Redux when you are trying to update the state method, it's your copying type — you likely need a deep copy instead of a shallow copy. In other words, a shallow clone only copies the top level of the object. You ask your best friend for recommendations and you like every  Cloning the Sith Way If we want to "clone like a Sith lord", there's no way that I know to accomplish it without going to the dark side. In other words, we must utilize recursion. Since every object/array can contain a theoretically-endless number of nested objects/arrays, we can't get by with a simple for/while loop.

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.

Handling State in React: Four Immutable Approaches to Consider, Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. This means a manual state mutation may be overridden when setState is processed Here are four ways to treat state as immutable: This creates a separate copy of the user object that's stored in state. Uncaught Error: Objects are not valid as a React child (found: object with keys {name, experience}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of `EditProfile`. My EditProfile render part for the instruments

Reactjs, I am representing these recipes as an array of objects in my main property on the main applications state just because this is how react likes to handle changes​. Here is a really good reference on how this should be handled: but one needs to keep in mind that copying an object that way, while deep,  In React , we all use setState to update the state . i was having some issue about updating the state of a array of Object to an empty state array. But there was i little bit of problem in that . i…

How to manage React State with Arrays, Learn how to manipulate arrays in React state by using JavaScript array an item to an array or how to update an item in an array, when React state is used to store it. it via the React component's class instance with the this object. "​Never mutate this.state directly, as calling setState() afterwards may  This happens because ItemList is pure, and because pushing a new item on the this.state.items array does not replace the underlying array. When ItemList is asked to re-render, it will notice that its props haven’t changed and it will not re-render. Recap. So there you go: that’s why you shouldn’t mutate state, even if you immediately call setState.

Immutability in React and Redux: The Complete Guide, At the end, we'll look at how to make it easier with a library it unwraps the children within, and inserts them right there. This is useful for creating a copy of an object/array, and then setState() , you don't need to spread the existing state like you would with Redux. /u/atticusw has a good example of doing this the manual (vanilla) way, which often depends on object spreading and array concat for arrays to make copies (with references intact) as you dig through your hierarchy.

Comments
  • but it seems like we are directly changing state since allItems is really pointing to this.state.items
  • Any reason why you don't just do this.state = { dogs: [], cats: [] }. Might make life a little easier.
  • when I try your map function I get Constructor Map requires 'new'
  • what does that mean?
  • i added { Map } from 'immutable'; in the very top line so that Map doesn't throw the error you mentioned above