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
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.
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:
This is also a mutation:
This is _not_ a mutation:
This is also _not_ a mutation:
In this example:
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
combineReducersdoes. 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 toObject.assignthe old state because every value is going to be different anyway.Object.assignis only useful if you change some values, but leave other values as they were.Does this help? These ways of writing it:
are completely equivalent (except
Object.assignseems more wasteful) ifcounterandotherCounterare the only keys. However, if you wroteand passed a
{ counter: 5, otherCounter: 42 }you'd get{ counter: 6 }in result, losingotherCounter. This is why we usually suggestObject.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.