Skip to content

Commit

Permalink
Merge pull request #856 from aaronjensen/combine-reducers-reference-e…
Browse files Browse the repository at this point in the history
…quality

Have combineReducers return same object if nothing changes
  • Loading branch information
gaearon committed Oct 7, 2015
2 parents 408ba41 + 6b8a4a8 commit 2cd6b2b
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 4 deletions.
11 changes: 7 additions & 4 deletions src/utils/combineReducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,16 @@ export default function combineReducers(reducers) {
throw sanityError;
}

var hasChanged = false;
var finalState = mapValues(finalReducers, (reducer, key) => {
var newState = reducer(state[key], action);
if (typeof newState === 'undefined') {
var previousStateForKey = state[key];
var nextStateForKey = reducer(previousStateForKey, action);
if (typeof nextStateForKey === 'undefined') {
var errorMessage = getUndefinedStateErrorMessage(key, action);
throw new Error(errorMessage);
}
return newState;
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
return nextStateForKey;
});

if (process.env.NODE_ENV !== 'production') {
Expand All @@ -129,6 +132,6 @@ export default function combineReducers(reducers) {
}
}

return finalState;
return hasChanged ? finalState : state;
};
}
39 changes: 39 additions & 0 deletions test/utils/combineReducers.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,45 @@ describe('Utils', () => {
expect(reducer({counter: 0}, { type: increment }).counter).toEqual(1);
});

it('should maintain referential equality if the reducers it is combining do', () => {
const reducer = combineReducers({
child1(state = {}) {
return state;
},
child2(state = {}) {
return state;
},
child3(state = {}) {
return state;
}
});

const initialState = reducer(undefined, '@@INIT');
expect(reducer(initialState, { type: 'FOO' })).toBe(initialState);
});

it('should not have referential equality if one of the reducers changes something', () => {
const reducer = combineReducers({
child1(state = {}) {
return state;
},
child2(state = { count: 0 }, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
default:
return state;
}
},
child3(state = {}) {
return state;
}
});

const initialState = reducer(undefined, '@@INIT');
expect(reducer(initialState, { type: 'increment' })).toNotBe(initialState);
});

it('should throw an error on first call if a reducer attempts to handle a private action', () => {
const reducer = combineReducers({
counter(state, action) {
Expand Down

0 comments on commit 2cd6b2b

Please sign in to comment.