Skip to content

Commit

Permalink
fix(StoreDevtools): Only recompute current state when reducers are up…
Browse files Browse the repository at this point in the history
…dated (#570)

Closes #229, #487
  • Loading branch information
brandonroberts authored and MikeRyanDev committed Nov 20, 2017
1 parent 5a998ba commit 247ae1a
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 5 deletions.
22 changes: 18 additions & 4 deletions modules/store-devtools/spec/store.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ describe('Store Devtools', () => {
expect(getState()).toBe(2);
});

it('should replace the reducer', () => {
it('should replace the reducer and preserve previous states', () => {
store.dispatch({ type: 'INCREMENT' });
store.dispatch({ type: 'DECREMENT' });
store.dispatch({ type: 'INCREMENT' });
Expand All @@ -268,7 +268,21 @@ describe('Store Devtools', () => {

fixture.replaceReducer(doubleCounter);

expect(getState()).toBe(2);
expect(getState()).toBe(1);
});

it('should replace the reducer and compute new state with latest reducer', () => {
store.dispatch({ type: 'INCREMENT' });
store.dispatch({ type: 'DECREMENT' });
store.dispatch({ type: 'INCREMENT' });

expect(getState()).toBe(1);

fixture.replaceReducer(doubleCounter);

store.dispatch({ type: 'INCREMENT' });

expect(getState()).toBe(3);
});

it('should catch and record errors', () => {
Expand All @@ -280,8 +294,8 @@ describe('Store Devtools', () => {
store.dispatch({ type: 'INCREMENT' });

let { computedStates } = fixture.getLiftedState();
expect(computedStates[2].error).toMatch(/ReferenceError/);
expect(computedStates[3].error).toMatch(
expect(computedStates[3].error).toMatch(/ReferenceError/);
expect(computedStates[4].error).toMatch(
/Interrupted by an error up the chain/
);

Expand Down
62 changes: 61 additions & 1 deletion modules/store-devtools/src/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { difference, liftAction } from './utils';
import * as Actions from './actions';
import { StoreDevtoolsConfig } from './config';
import { PerformAction } from './actions';

export type InitAction = {
readonly type: typeof INIT;
Expand Down Expand Up @@ -300,7 +301,6 @@ export function liftReducerWith(
} = liftedAction.nextLiftedState);
break;
}
case UPDATE:
case INIT: {
// Always recompute states on hot reload and init.
minInvalidatedStateIndex = 0;
Expand All @@ -325,6 +325,66 @@ export function liftReducerWith(

break;
}
case UPDATE: {
const stateHasErrors =
computedStates.filter(state => state.error).length > 0;

if (stateHasErrors) {
// Recompute all states
minInvalidatedStateIndex = 0;

if (options.maxAge && stagedActionIds.length > options.maxAge) {
// States must be recomputed before committing excess.
computedStates = recomputeStates(
computedStates,
minInvalidatedStateIndex,
reducer,
committedState,
actionsById,
stagedActionIds,
skippedActionIds
);

commitExcessActions(stagedActionIds.length - options.maxAge);

// Avoid double computation.
minInvalidatedStateIndex = Infinity;
}
} else {
if (currentStateIndex === stagedActionIds.length - 1) {
currentStateIndex++;
}

// Add a new action to only recompute state
const actionId = nextActionId++;
actionsById[actionId] = new PerformAction(liftedAction);
stagedActionIds = [...stagedActionIds, actionId];

minInvalidatedStateIndex = stagedActionIds.length - 1;

// States must be recomputed before committing excess.
computedStates = recomputeStates(
computedStates,
minInvalidatedStateIndex,
reducer,
committedState,
actionsById,
stagedActionIds,
skippedActionIds
);

currentStateIndex = minInvalidatedStateIndex;

if (options.maxAge && stagedActionIds.length > options.maxAge) {
commitExcessActions(stagedActionIds.length - options.maxAge);
}

// Avoid double computation.
minInvalidatedStateIndex = Infinity;
}

break;
}
default: {
// If the action is not recognized, it's a monitor action.
// Optimization: a monitor action can't change history.
Expand Down

0 comments on commit 247ae1a

Please sign in to comment.