Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
  }
}
like image 957
mangocaptain Avatar asked Aug 29 '16 06:08

mangocaptain


People also ask

How do I change state in reducer Redux?

The only way to update a state inside a store is to dispatch an action and define a reducer function to perform tasks based on the given actions. Once dispatched, the action goes inside the reducer functions which performs the tasks and return the updated state to the store.

Why do we need to copy the state in a reducer?

If the new state is different, the reducer must create new object, and making a copy is a way to describe the unchanged part.

Can I mutate state in reducer?

Don't Mutate State Since one of the core tenets of Redux is to never mutate state, you'll often find yourself using Object. assign() to create copies of objects with new or updated values. If you remember, Object. assign() is a function that takes any number of arguments.

Should reducer always return state?

Reducers always have to return something even if it's null ; they should never return undefined . If a reducer's state is an object, you must always return a new object instead of editing the object in place.


2 Answers

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.

like image 101
Tim Wright Avatar answered Sep 28 '22 09:09

Tim Wright


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.

like image 24
Md.Estiak Ahmmed Avatar answered Sep 28 '22 08:09

Md.Estiak Ahmmed