mangocaptain
mangocaptain

Reputation: 1495

Replacing new state in react redux reducer without making a copy

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

Answers (2)

Tim Wright
Tim Wright

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

Md.Estiak Ahmmed
Md.Estiak Ahmmed

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

Related Questions