I know I'm not supposed to mutate the input and should clone the object to mutate it. I was following the convention used on a redux starter project which used:
ADD_ITEM: (state, action) => ({
...state,
items: [...state.items, action.payload.value],
lastUpdated: action.payload.date
})
for adding an item - I get the use of spread to append the item in the array.
for deleting I used:
DELETE_ITEM: (state, action) => ({
...state,
items: [...state.items.splice(0, action.payload), ...state.items.splice(1)],
lastUpdated: Date.now()
})
but this is mutating the input state object - is this forbidden even though I am returning a new object?
No. Never mutate your state.
Even though you're returning a new object, you're still polluting the old object, which you never want to do. This makes it problematic when doing comparisons between the old and the new state. For instance in shouldComponentUpdate
which react-redux uses under the hood. It also makes time travel impossible (i.e. undo and redo).
Instead, use immutable methods. Always use Array#slice
and never Array#splice
.
I assume from your code that action.payload
is the index of the item being removed. A better way would be as follows:
items: [
...state.items.slice(0, action.payload),
...state.items.slice(action.payload + 1)
],
You can use the array filter method to remove a specific element from an array without mutating the original state.
return state.filter(element => element !== action.payload);
In the context of your code, it would look something like this:
DELETE_ITEM: (state, action) => ({
...state,
items: state.items.filter(item => item !== action.payload),
lastUpdated: Date.now()
})
The ES6 Array.prototype.filter
method returns a new array with the items that match the criteria. Therefore, in the context of the original question, this would be:
DELETE_ITEM: (state, action) => ({
...state,
items: state.items.filter(item => action.payload !== item),
lastUpdated: Date.now()
})
Another one variation of the immutable "DELETED" reducer for the array with objects:
const index = state.map(item => item.name).indexOf(action.name);
const stateTemp = [
...state.slice(0, index),
...state.slice(index + 1)
];
return stateTemp;