Until recently, I've always just used lodash's cloneDeep to make a copy of my state, then change values and return the cloned state. For example like this:
This would be my initial state:
{
"id": 1213,
"title": "Some title...",
"pages": {
"page1": {
"id": 459,
"title": "Some Page title...",
"fields": {
"field_1": {
"title": "My field",
"type": "text",
"value": "my text value..."
},
"field_2": {
"title": "My field 2",
"type": "text",
"value": "my text two value..."
},
"field_3": {
"title": "My field 3",
"type": "text",
"value": "my text value..."
}
}
}
}
}
Now, I want to update the value of field_2.
My redux reducer would look like this:
import cloneDeep from 'lodash/fp/cloneDeep';
export default function reducer(state, action) {
const {type, payload} = action;
switch (type) {
case 'UPDATE_FIELD_VALUE': {
const { pageIdent, fieldIdent, newValue } = payload;
// This is what I'm doing right now....
const newState = cloneDeep(state);
newState.pages[pageIdent]fields[fieldIdent]value = newValue;
return newState;
// Instead could I do this?
return {
...state,
state.pages[pageIdent]fields[fieldIdent]value = newValue;
}
}
}
}
So, I've read that I don't always have to do deep clone...but in other places I've read that you cannot return the same object, you have to return new objects at all times. So what is the right way to do this?
Yeah, don't do that. Quoting the Redux FAQ on whether you should deep-clone state:
Immutably updating state generally means making shallow copies, not deep copies. Shallow copies are much faster than deep copies, because fewer objects and fields have to be copied, and it effectively comes down to moving some pointers around.
However, you do need to create a copied and updated object for each level of nesting that is affected. Although that shouldn't be particularly expensive, it's another good reason why you should keep your state normalized and shallow if possible.
Common Redux misconception: you need to deeply clone the state. Reality: if something inside doesn't change, keep its reference the same!
So, you don't want "deep clones", you need "nested shallow clones".
Deep-cloning is bad for performance in two ways: it takes more work to clone everything, and the new object references will cause UI updates for data that didn't actually change in value (but the new references make the UI think that something changed).
You should read the Redux docs page on "Immutable Update Patterns". Here's the nested state update example from that page:
function updateVeryNestedField(state, action) {
return {
....state,
first : {
...state.first,
second : {
...state.first.second,
[action.someId] : {
...state.first.second[action.someId],
fourth : action.someValue
}
}
}
}
}
If you find that to be too tedious or painful, you should either change how you structure your state so it's flatter, or you can use one of the many immutable update utility libraries out there to handle the update process for you.
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