Reputation: 1495
If I have completely replacing the entirety of a slice of state, do I still have to use Object.assign or the spread operator to make a copy of the original state and replace it with the new state, or can I just return the new state in my reducer?
const fetching = (state = { isFetching: false }, action) => {
switch (action.type) {
case 'REQUESTING':
return Object.assign({}, state, { isFetching: true } )
case 'RECEIVE_POKEMON_TYPE_INFO':
return Object.assign({}, state, { isFetching: false } )
default:
return state
}
}
vs.
const fetching = (state = { isFetching: false }, action) => {
switch (action.type) {
case 'REQUESTING':
return { isFetching: true }
case 'RECEIVE_POKEMON_TYPE_INFO':
return { isFetching: false }
default:
return state
}
}
Upvotes: 5
Views: 5459
Reputation: 109
There are a couple of things going on here. Basically, if your state only consists of a boolean variable, then creating a new object by enumeration is OK. However, if your state consists of other things, then doing an object.assign should work.
However (and isn't there always a 'however'), if your state is complex - that is it consists of other objects then doing object.assign will not properly copy the fields - it copied the references not the values. For example, if your state consists of a "currentlySelectedPokemon" field there the value is a Pokemon object, then Object.assign will copy a reference to the pokemon object. It won't copy the object itself. To show this more easily, look at the code below - it prints "value2" for obj2.
var obj1 = {
field: {
subfield: "value"
}
};
var obj2 = Object.assign({}, obj1);
obj1.field.subfield = "value2";
console.log(JSON.stringify(obj2, null, 2));
There are two ways to get around this. The first is to use the Immutable library for all your state. However, I found the overhead of converting complex objects into Immutable and back provided enough complexity that it introduced unnecessary bugs. So now I do this:
const fetching = (state = { isFetching: false }, action) => {
switch (action.type) {
case 'REQUESTING':
const newState = JSON.parse(JSON.stringify(state));
newState.isFetching = true;
return newState;
case 'RECEIVE_POKEMON_TYPE_INFO':
const newState = JSON.parse(JSON.stringify(state));
newState.isFetching = false;
return newState;
default:
return state
}
}
It turns out that JSON.parse(JSON.stringify(object)) is a fast reliable way to make a copy of a vanilla java object. It strips all functions (which is what I generally want). And it's fast because browsers usually implement those functions in native code.
Upvotes: 6
Reputation: 1593
you can either use Object.assign
or spread operator
, like below
using Object.assign
:
const fetching = (state = { isFetching: false }, action) => {
switch (action.type) {
case 'REQUESTING':
return Object.assign({}, state, { isFetching: true } )
case 'RECEIVE_POKEMON_TYPE_INFO':
return Object.assign({}, state, { isFetching: false } )
default:
return state
}
}
using spread operator:
const fetching = (state = { isFetching: false }, action) => {
switch (action.type) {
case 'REQUESTING':
return { ...state, isFetching: true }
case 'RECEIVE_POKEMON_TYPE_INFO':
return { ...state, isFetching: false }
default:
return state
}
}
using Object.assign()
can quickly make simple reducers difficult to read given its rather verbose syntax
.the spread (...)
operator to copy enumerable properties from one object to another in a more succinct way.
Upvotes: 1