Redux: Reducer question

Created on 29 Sep 2015  Â·  1Comment  Â·  Source: reduxjs/redux

I have readed the basic doc to learn how redux works, and I have a question.

The document has a premise: : Just remember to never assign to anything inside the state unless you clone it first.

In https://github.com/rackt/redux/blame/master/docs/basics/Reducers.md#L213-L227

the return is:

return Object.assign({}, state, {
todos: todos(state.todos, action)
});

And in https://github.com/rackt/redux/blob/master/docs/basics/Reducers.md#L277-L282 the return is

return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: todos(state.todos, action)
};

Why the second one doesn't have a clone like:

  return Object.assign({}, state, {
    visibilityFilter: visibilityFilter(state.visibilityFilter, action),
    todos: todos(state.todos, action)
  });

I'm confused on how the combineReducers makes a clone.

Any help? Thanks

question

Most helpful comment

The cloning is not the important part; the important part is _not mutating_ the state given to you.

This is a mutation:

state.something = state.something + 1;
return state;

This is also a mutation:

Object.assign(state, {
  something: state.something + 1
});
return state;

This is _not_ a mutation:

let nextState = {
  something: state.something + 1
};
return nextState;

This is also _not_ a mutation:

let nextState = Object.assign({}, state, {
  something: state.something + 1
});
return nextState;

In this example:

return {
  visibilityFilter: visibilityFilter(state.visibilityFilter, action),
  todos: todos(state.todos, action)
};

A new object is always being created (return {}).
Therefore there is no need to clone it—it's already a new object.

This is also what combineReducers does. It always creates a new object and doesn't bother copying the old state because it already knows all the keys and doesn't need to Object.assign the old state because every value is going to be different anyway. Object.assign is only useful if you change some values, but leave other values as they were.

Does this help? These ways of writing it:

return {
  counter: state.counter + 1,
  otherCounter: state.otherCounter + 1
};

return Object.assign({}, state, {
  counter: state.counter + 1,
  otherCounter: state.otherCounter + 1
});

are completely equivalent (except Object.assign seems more wasteful) if counter and otherCounter are the only keys. However, if you wrote

return {
  counter: state.counter + 1
};

and passed a { counter: 5, otherCounter: 42 } you'd get { counter: 6 } in result, losing otherCounter. This is why we usually suggest Object.assign() in the docs, so that you don't lose the rest of the state when doing updates to specific keys. As I said before, it's irrelevant for cases when you already know all the keys and always update each of them.

>All comments

The cloning is not the important part; the important part is _not mutating_ the state given to you.

This is a mutation:

state.something = state.something + 1;
return state;

This is also a mutation:

Object.assign(state, {
  something: state.something + 1
});
return state;

This is _not_ a mutation:

let nextState = {
  something: state.something + 1
};
return nextState;

This is also _not_ a mutation:

let nextState = Object.assign({}, state, {
  something: state.something + 1
});
return nextState;

In this example:

return {
  visibilityFilter: visibilityFilter(state.visibilityFilter, action),
  todos: todos(state.todos, action)
};

A new object is always being created (return {}).
Therefore there is no need to clone it—it's already a new object.

This is also what combineReducers does. It always creates a new object and doesn't bother copying the old state because it already knows all the keys and doesn't need to Object.assign the old state because every value is going to be different anyway. Object.assign is only useful if you change some values, but leave other values as they were.

Does this help? These ways of writing it:

return {
  counter: state.counter + 1,
  otherCounter: state.otherCounter + 1
};

return Object.assign({}, state, {
  counter: state.counter + 1,
  otherCounter: state.otherCounter + 1
});

are completely equivalent (except Object.assign seems more wasteful) if counter and otherCounter are the only keys. However, if you wrote

return {
  counter: state.counter + 1
};

and passed a { counter: 5, otherCounter: 42 } you'd get { counter: 6 } in result, losing otherCounter. This is why we usually suggest Object.assign() in the docs, so that you don't lose the rest of the state when doing updates to specific keys. As I said before, it's irrelevant for cases when you already know all the keys and always update each of them.

Was this page helpful?
0 / 5 - 0 ratings