1+ import difference from 'lodash/array/difference' ;
2+
13export const ActionTypes = {
24 PERFORM_ACTION : 'PERFORM_ACTION' ,
35 RESET : 'RESET' ,
@@ -33,8 +35,8 @@ export const ActionCreators = {
3335 return { type : ActionTypes . SWEEP } ;
3436 } ,
3537
36- toggleAction ( index ) {
37- return { type : ActionTypes . TOGGLE_ACTION , index } ;
38+ toggleAction ( id ) {
39+ return { type : ActionTypes . TOGGLE_ACTION , id } ;
3840 } ,
3941
4042 jumpToState ( index ) {
@@ -48,16 +50,6 @@ export const ActionCreators = {
4850
4951const INIT_ACTION = { type : '@@INIT' } ;
5052
51- function toggle ( obj , key ) {
52- const clone = { ...obj } ;
53- if ( clone [ key ] ) {
54- delete clone [ key ] ;
55- } else {
56- clone [ key ] = true ;
57- }
58- return clone ;
59- }
60-
6153/**
6254 * Computes the next entry in the log by applying an action.
6355 */
@@ -87,17 +79,17 @@ function computeNextEntry(reducer, action, state, error) {
8779/**
8880 * Runs the reducer on all actions to get a fresh computation log.
8981 */
90- function recomputeStates ( reducer , committedState , stagedActions , skippedActions ) {
82+ function recomputeStates ( reducer , committedState , actionsById , stagedActionIds , skippedActionIds ) {
9183 const computedStates = [ ] ;
92-
93- for ( let i = 0 ; i < stagedActions . length ; i ++ ) {
94- const action = stagedActions [ i ] ;
84+ for ( let i = 0 ; i < stagedActionIds . length ; i ++ ) {
85+ const actionId = stagedActionIds [ i ] ;
86+ const action = actionsById [ actionId ] . action ;
9587
9688 const previousEntry = computedStates [ i - 1 ] ;
9789 const previousState = previousEntry ? previousEntry . state : committedState ;
9890 const previousError = previousEntry ? previousEntry . error : undefined ;
9991
100- const shouldSkip = Boolean ( skippedActions [ i ] ) ;
92+ const shouldSkip = skippedActionIds . indexOf ( actionId ) > - 1 ;
10193 const entry = shouldSkip ?
10294 previousEntry :
10395 computeNextEntry ( reducer , action , previousState , previousError ) ;
@@ -108,17 +100,28 @@ function recomputeStates(reducer, committedState, stagedActions, skippedActions)
108100 return computedStates ;
109101}
110102
103+ /**
104+ * Lifts an app's action into an action on the lifted store.
105+ */
106+ function liftAction ( action ) {
107+ return ActionCreators . performAction ( action ) ;
108+ }
109+
111110/**
112111 * Creates a history state reducer from an app's reducer.
113112 */
114113function liftReducerWith ( reducer , initialCommittedState , monitorReducer ) {
115114 const initialLiftedState = {
115+ monitorState : monitorReducer ( undefined , { } ) ,
116+ nextActionId : 1 ,
117+ actionsById : {
118+ 0 : liftAction ( INIT_ACTION )
119+ } ,
120+ stagedActionIds : [ 0 ] ,
121+ skippedActionIds : [ ] ,
116122 committedState : initialCommittedState ,
117- stagedActions : [ INIT_ACTION ] ,
118- skippedActions : { } ,
119123 currentStateIndex : 0 ,
120- timestamps : [ Date . now ( ) ] ,
121- monitorState : monitorReducer ( undefined , { } )
124+ computedStates : undefined
122125 } ;
123126
124127 /**
@@ -128,57 +131,79 @@ function liftReducerWith(reducer, initialCommittedState, monitorReducer) {
128131 let shouldRecomputeStates = true ;
129132 let {
130133 monitorState,
134+ actionsById,
135+ nextActionId,
136+ stagedActionIds,
137+ skippedActionIds,
131138 committedState,
132- stagedActions,
133- skippedActions,
134- computedStates,
135139 currentStateIndex,
136- timestamps
140+ computedStates
137141 } = liftedState ;
138142
139143 switch ( liftedAction . type ) {
140144 case ActionTypes . RESET :
145+ actionsById = {
146+ 0 : liftAction ( INIT_ACTION )
147+ } ;
148+ nextActionId = 1 ;
149+ stagedActionIds = [ 0 ] ;
150+ skippedActionIds = [ ] ;
141151 committedState = initialCommittedState ;
142- stagedActions = [ INIT_ACTION ] ;
143- skippedActions = { } ;
144152 currentStateIndex = 0 ;
145- timestamps = [ liftedAction . timestamp ] ;
146153 break ;
147154 case ActionTypes . COMMIT :
155+ actionsById = {
156+ 0 : liftAction ( INIT_ACTION )
157+ } ;
158+ nextActionId = 1 ;
159+ stagedActionIds = [ 0 ] ;
160+ skippedActionIds = [ ] ;
148161 committedState = computedStates [ currentStateIndex ] . state ;
149- stagedActions = [ INIT_ACTION ] ;
150- skippedActions = { } ;
151162 currentStateIndex = 0 ;
152- timestamps = [ liftedAction . timestamp ] ;
153163 break ;
154164 case ActionTypes . ROLLBACK :
155- stagedActions = [ INIT_ACTION ] ;
156- skippedActions = { } ;
165+ actionsById = {
166+ 0 : liftAction ( INIT_ACTION )
167+ } ;
168+ nextActionId = 1 ;
169+ stagedActionIds = [ 0 ] ;
170+ skippedActionIds = [ ] ;
157171 currentStateIndex = 0 ;
158- timestamps = [ liftedAction . timestamp ] ;
159172 break ;
160173 case ActionTypes . TOGGLE_ACTION :
161- skippedActions = toggle ( skippedActions , liftedAction . index ) ;
174+ const index = skippedActionIds . indexOf ( liftedAction . id ) ;
175+ if ( index === - 1 ) {
176+ skippedActionIds = [
177+ liftedAction . id ,
178+ ...skippedActionIds
179+ ] ;
180+ } else {
181+ skippedActionIds = [
182+ ...skippedActionIds . slice ( 0 , index ) ,
183+ ...skippedActionIds . slice ( index + 1 )
184+ ] ;
185+ }
162186 break ;
163187 case ActionTypes . JUMP_TO_STATE :
164188 currentStateIndex = liftedAction . index ;
165189 // Optimization: we know the history has not changed.
166190 shouldRecomputeStates = false ;
167191 break ;
168192 case ActionTypes . SWEEP :
169- stagedActions = stagedActions . filter ( ( _ , i ) => ! skippedActions [ i ] ) ;
170- timestamps = timestamps . filter ( ( _ , i ) => ! skippedActions [ i ] ) ;
171- skippedActions = { } ;
172- currentStateIndex = Math . min ( currentStateIndex , stagedActions . length - 1 ) ;
193+ stagedActionIds = difference ( stagedActionIds , skippedActionIds ) ;
194+ skippedActionIds = [ ] ;
195+ currentStateIndex = Math . min ( currentStateIndex , stagedActionIds . length - 1 ) ;
173196 break ;
174197 case ActionTypes . PERFORM_ACTION :
175- if ( currentStateIndex === stagedActions . length - 1 ) {
198+ if ( currentStateIndex === stagedActionIds . length - 1 ) {
176199 currentStateIndex ++ ;
177200 }
178201
179- stagedActions = [ ...stagedActions , liftedAction . action ] ;
180- timestamps = [ ...timestamps , liftedAction . timestamp ] ;
181-
202+ const actionId = nextActionId ++ ;
203+ // Mutation! This is the hottest path, and we optimize on purpose.
204+ // It is safe because we set a new key in a cache dictionary.
205+ actionsById [ actionId ] = liftedAction ;
206+ stagedActionIds = [ ...stagedActionIds , actionId ] ;
182207 // Optimization: we know that the past has not changed.
183208 shouldRecomputeStates = false ;
184209 // Instead of recomputing the states, append the next one.
@@ -193,12 +218,14 @@ function liftReducerWith(reducer, initialCommittedState, monitorReducer) {
193218 break ;
194219 case ActionTypes . IMPORT_STATE :
195220 ( {
196- stagedActions,
197- skippedActions,
198- computedStates,
221+ monitorState,
222+ actionsById,
223+ nextActionId,
224+ stagedActionIds,
225+ skippedActionIds,
226+ committedState,
199227 currentStateIndex,
200- timestamps,
201- monitorState
228+ computedStates
202229 } = liftedAction . nextLiftedState ) ;
203230 break ;
204231 case '@@redux/INIT' :
@@ -215,21 +242,23 @@ function liftReducerWith(reducer, initialCommittedState, monitorReducer) {
215242 computedStates = recomputeStates (
216243 reducer ,
217244 committedState ,
218- stagedActions ,
219- skippedActions
245+ actionsById ,
246+ stagedActionIds ,
247+ skippedActionIds
220248 ) ;
221249 }
222250
223251 monitorState = monitorReducer ( monitorState , liftedAction ) ;
224252
225253 return {
254+ monitorState,
255+ actionsById,
256+ nextActionId,
257+ stagedActionIds,
258+ skippedActionIds,
226259 committedState,
227- stagedActions,
228- skippedActions,
229- computedStates,
230260 currentStateIndex,
231- timestamps,
232- monitorState
261+ computedStates
233262 } ;
234263 } ;
235264}
@@ -243,13 +272,6 @@ function unliftState(liftedState) {
243272 return state ;
244273}
245274
246- /**
247- * Lifts an app's action into an action on the lifted store.
248- */
249- function liftAction ( action ) {
250- return ActionCreators . performAction ( action ) ;
251- }
252-
253275/**
254276 * Provides an app's view into the lifted store.
255277 */
0 commit comments