Reputation: 535
I am trying to change one item in of an array in my state using a reducer.
State looks like:
state: {
items: [
{
id: 1,
name: 'Superman',
wearsCape: true
},
{
id: 2,
name: 'Batman',
wearsCape: true
},
{
id: 3,
name: 'Iron Man',
wearsCape: false
}
];
}
I am trying to filter through all the items in the state to search for first occurance of a superhero that has name equal sHero.name
. Then I am trying to change a property of found superhero. The reducer code looks like:
function findCapedCrusader(state, { sHero }) {
var result = state.items.filter(s => {
return s.name === sHero.name;
});
result[0].wearsCape = false;
return { ...state };
}
Am I mutating the state by doing the above??
NOTE: apologies for stupid example. I am new to react and redux.
Upvotes: 4
Views: 1436
Reputation: 15652
Yes you are mutating your state in this example. While filter
does produce a new array, it is really only a shallow copy of the original. The elements of your state.items
array are objects, so when you modify one in your new results
array (which contains pointers to the original contents of state.items
) you modify the original object. See this answer for a bit more on deep vs shallow copies: What is the difference between a shallow copy and a deep copy with JavaScript arrays?
To fix this you can either:
1. Use the spread operator on result
and then overwrite the value of wearsCape
like this:
result = state.items.filter(s => s.name === sHero.name).map(s => {
return {...s, wearsCape: false}
});
This method works with serializable and non-serializable data and is the recommended way to update nested state values.
Or
2. Make a deep copy of state.items
before filtering:
const results = JSON.parse(JSON.stringify(state.items)).filter(s => {
return s.name === sHero.name;
});
This method is a bit strange but works well for serializable data in cases where the value you want to modify is deeply nested and using the spread operator (...
) many times would become cumbersome.
Upvotes: 0
Reputation: 17598
As far as I know, filter
method returns a new array but the returned elements still reference the old ones. So, if someone mutates a property to the newly created array they also mutate the original one.
const state = {
items: [
{
id: 1,
name: 'Superman',
wearsCape: true
},
{
id: 2,
name: 'Batman',
wearsCape: true
},
{
id: 3,
name: 'Iron Man',
wearsCape: false
}
]
};
const newItem = state.items.filter( s => s.name === "Superman");
newItem[0].wearsCape = false;
console.log( "newitem", newItem, "\noriginal", state.items[0] );
If you want to change one object's property in the array and update your state:
const state = {
items: [
{
id: 1,
name: 'Superman',
wearsCape: true
},
{
id: 2,
name: 'Batman',
wearsCape: true
},
{
id: 3,
name: 'Iron Man',
wearsCape: false
}
]
};
const newItems = state.items.map( s => {
if( s.name === "Superman") {
return { ...s, wearsCape: false };
}
return s;
});
console.log( "new items", newItems, "original", state.items );
As you can see, the original items
is not mutated. For your situation it will be something like that:
function findCapedCrusader(state, { sHero }) {
var newItems = state.items.map(s => {
if (s.name === sHero.name ) {
return { ...s, wearsCape = false }
}
return s;
});
return { ...state, items: newItems };
}
Again, this function will change one object's property and does not change anything else. I'm not quite sure this is what you want since you are trying a filter
in your question. If you want to update your state with one single object, I can suggest another solution.
Upvotes: 1
Reputation: 1195
You are. Even though the filter function will produce a new array, the state is in the objects you return in that new array. Based on that, you are changing the state, which does not comply which what a pure function is.
Here I leave you with a good post that will help you to understand better what a deep copy is and how to accomplish it.
https://medium.com/@tkssharma/objects-in-javascript-object-assign-deep-copy-64106c9aefab
Upvotes: 0
Reputation: 30360
The filter()
method is returning a new array instance, so you are not mutating your state. For more information, see the MDN documentation:
Return value
A new array with each element being the result of the callback function.
Upvotes: 0