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?
Hence, setState() doesn't perform a deep copy. It merely performs a shallow merge.
Arrays are mutable in JavaScript, but you should treat them as immutable when you store them in state. Just like with objects, when you want to update an array stored in state, you need to create a new one (or make a copy of an existing one), and then set state to use the new array.
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});
}
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With