From 0facdbbc9f41088cf220fe3cb5988de2f7dbfe34 Mon Sep 17 00:00:00 2001 From: echenley Date: Thu, 4 Feb 2016 13:41:45 -0600 Subject: [PATCH] add maxAge option --- src/createDevTools.js | 8 ++++---- src/instrument.js | 16 +++++++++++++--- test/instrument.spec.js | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/createDevTools.js b/src/createDevTools.js index 9e1381af..41ad6f54 100644 --- a/src/createDevTools.js +++ b/src/createDevTools.js @@ -7,9 +7,6 @@ export default function createDevTools(children) { const monitorProps = monitorElement.props; const Monitor = monitorElement.type; const ConnectedMonitor = connect(state => state)(Monitor); - const enhancer = instrument((state, action) => - Monitor.update(monitorProps, state, action) - ); return class DevTools extends Component { static contextTypes = { @@ -20,7 +17,10 @@ export default function createDevTools(children) { store: PropTypes.object }; - static instrument = () => enhancer; + static instrument = (options) => instrument( + (state, action) => Monitor.update(monitorProps, state, action), + options + ); constructor(props, context) { super(props, context); diff --git a/src/instrument.js b/src/instrument.js index 7195979a..d12d998a 100644 --- a/src/instrument.js +++ b/src/instrument.js @@ -138,7 +138,7 @@ function liftAction(action) { /** * Creates a history state reducer from an app's reducer. */ -function liftReducerWith(reducer, initialCommittedState, monitorReducer) { +function liftReducerWith(reducer, initialCommittedState, monitorReducer, options) { const initialLiftedState = { monitorState: monitorReducer(undefined, {}), nextActionId: 1, @@ -235,6 +235,14 @@ function liftReducerWith(reducer, initialCommittedState, monitorReducer) { break; } case ActionTypes.PERFORM_ACTION: { + if (options.maxAge && stagedActionIds.length === options.maxAge) { + // If maxAge has been reached, remove oldest action + delete actionsById[stagedActionIds[0]]; + skippedActionIds = skippedActionIds.filter(id => id !== stagedActionIds[0]); + stagedActionIds = stagedActionIds.slice(1); + committedState = computedStates[1].state; + computedStates = computedStates.slice(1); + } if (currentStateIndex === stagedActionIds.length - 1) { currentStateIndex++; } @@ -339,7 +347,9 @@ function unliftStore(liftedStore, liftReducer) { /** * Redux instrumentation store enhancer. */ -export default function instrument(monitorReducer = () => null) { +export default function instrument(monitorReducer, options = {}) { + if (!monitorReducer) { monitorReducer = () => null; } + return createStore => (reducer, initialState, enhancer) => { function liftReducer(r) { @@ -354,7 +364,7 @@ export default function instrument(monitorReducer = () => null) { } throw new Error('Expected the reducer to be a function.'); } - return liftReducerWith(r, initialState, monitorReducer); + return liftReducerWith(r, initialState, monitorReducer, options); } const liftedStore = createStore(liftReducer(reducer), enhancer); diff --git a/test/instrument.spec.js b/test/instrument.spec.js index c8abc4af..b3083438 100644 --- a/test/instrument.spec.js +++ b/test/instrument.spec.js @@ -295,6 +295,44 @@ describe('instrument', () => { expect(monitoredLiftedStore.getState().computedStates).toBe(savedComputedStates); }); + describe('maxAge option', () => { + let configuredStore; + let configuredLiftedStore; + + beforeEach(() => { + configuredStore = createStore(counter, instrument(null, { maxAge: 2 })); + configuredLiftedStore = configuredStore.liftedStore; + }); + + it('should remove earliest action when maxAge is reached', () => { + configuredStore.dispatch({ type: 'INCREMENT' }); + let liftedStoreState = configuredLiftedStore.getState(); + + expect(configuredStore.getState()).toBe(1); + expect(Object.keys(liftedStoreState.actionsById).length).toBe(2); + expect(liftedStoreState.committedState).toBe(undefined); + + configuredStore.dispatch({ type: 'INCREMENT' }); + liftedStoreState = configuredLiftedStore.getState(); + + expect(configuredStore.getState()).toBe(2); + expect(Object.keys(liftedStoreState.actionsById).length).toBe(2); + expect(liftedStoreState.stagedActionIds).toExclude(0); + expect(liftedStoreState.computedStates[0].state).toBe(1); + expect(liftedStoreState.committedState).toBe(1); + expect(liftedStoreState.currentStateIndex).toBe(1); + }); + + it('should handle skipped actions', () => { + configuredStore.dispatch({ type: 'INCREMENT' }); + configuredLiftedStore.dispatch(ActionCreators.toggleAction(1)); + configuredStore.dispatch({ type: 'INCREMENT' }); + expect(configuredLiftedStore.getState().skippedActionIds).toInclude(1); + configuredStore.dispatch({ type: 'INCREMENT' }); + expect(configuredLiftedStore.getState().skippedActionIds).toExclude(1); + }); + }); + describe('Import State', () => { let monitoredStore; let monitoredLiftedStore;