Reputation: 82
I have a state array that I want to perform actions on and then update the state, but it seems like I'm missing a deep copy somewhere and the state get updated without setState which causes me problems (I know that because I see the state gets updated even without the setState line) .
const itemsToRemove = ['Pepsi', 'Fanta'];
let temp = [...this.state.categories];
let categoryToUpdate = temp.find((category) => category.name == 'drinks');
categoryToUpdate.items = categoryToUpdate.items.filter((item) => {
return !itemsToRemove.includes(item);
});
//this.setState({categories: temp})
After those lines I would like categories
state in object with "drinks" name to still include the items in itemsToRemove
because I did not update the state but they still get removed from the state.
categories
state's structure is as follows:
categories:[{name: String, items: Object}...]
Upvotes: 1
Views: 979
Reputation: 23160
You guessed it right, you still mutate the items
array when you update the copy.items
directly. Although you used the spread operator, it only goes one level deep. It only creates a shallow copy of the array.
It works great on primitive data types (strings, numbers, etc.) but when you have nested objects (arrays) and you copy it, these nested objects inside that object will not get copied as they are only references.
const original = [{
name: 'Name 1',
items: ['1', '2', '3']
}, {
name: 'Name 2',
items: ['11', '12', '13']
}, 99]
const copy = [...original]
// notice how the original third element won't change to 100
copy[2] = 100
// while all the items in the second element will
copy[1].items = ['21', '22', '23']
// and if you update a string inside the object
// that will be changed too as it's inside an object
copy[1].name = 'Updated name 2'
console.log('updated', copy)
console.log('original', original)
You can use Array.map()
to create a new array from the categories
array and as you loop through the elements, you can simply return a new object using the spread operator if you want to update them.
Check the snippet below:
const itemsToRemove = ['Pepsi', 'Fanta'];
const categories = [{
name: 'drinks',
items: ['1', '2', 'Pepsi', '4', 'Fanta', '6']
},
{
name: 'biscuits',
items: ['1', '2', '3', '4']
},
];
const updatedCategories = categories.map((category) =>
category.name === 'drinks'
? {
...category,
items: category.items.filter((item) => !itemsToRemove.includes(item)),
}
: category,
);
console.log('updated', updatedCategories);
console.log('original', categories);
Upvotes: 1