| 
          import { createStore, combineReducers, applyMiddleware } from 'redux'; | 
        
        
           | 
          
 | 
        
        
           | 
          // Actions | 
        
        
           | 
          const RECEIVE_UPDATE = 'RECEIVE_UPDATE'; | 
        
        
           | 
          function receiveUpdate(counter) { | 
        
        
           | 
              return { | 
        
        
           | 
                  type: RECEIVE_UPDATE, | 
        
        
           | 
                  payload: { | 
        
        
           | 
                      counter | 
        
        
           | 
                  } | 
        
        
           | 
              }; | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          const UNDO = 'UNDO'; | 
        
        
           | 
          function undo() { | 
        
        
           | 
              return { | 
        
        
           | 
                  type: UNDO | 
        
        
           | 
              } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          function add(value) { | 
        
        
           | 
              return (dispatch, getState) => { | 
        
        
           | 
                  const { counter } = getState(); | 
        
        
           | 
                  const newValue = counter + value; | 
        
        
           | 
          
 | 
        
        
           | 
                  return new Promise((resolve, reject) => { | 
        
        
           | 
                      resolve(newValue); | 
        
        
           | 
                  }).then((data) => { | 
        
        
           | 
                      dispatch(receiveUpdate(data)); | 
        
        
           | 
                  }); | 
        
        
           | 
              } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          function sub(value) { | 
        
        
           | 
              return (dispatch, getState) => { | 
        
        
           | 
                  const { counter } = getState(); | 
        
        
           | 
                  const newValue = counter - value; | 
        
        
           | 
          
 | 
        
        
           | 
                  return new Promise((resolve, reject) => { | 
        
        
           | 
                      resolve(newValue); | 
        
        
           | 
                  }).then((data) => { | 
        
        
           | 
                      dispatch(receiveUpdate(data)); | 
        
        
           | 
                  }); | 
        
        
           | 
              } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          function mul(value) { | 
        
        
           | 
              return (dispatch, getState) => { | 
        
        
           | 
                  const { counter } = getState(); | 
        
        
           | 
                  const newValue = counter * value; | 
        
        
           | 
          
 | 
        
        
           | 
                  return new Promise((resolve, reject) => { | 
        
        
           | 
                      resolve(newValue); | 
        
        
           | 
                  }).then((data) => { | 
        
        
           | 
                      dispatch(receiveUpdate(data)); | 
        
        
           | 
                  }); | 
        
        
           | 
              } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          function div(value) { | 
        
        
           | 
              return (dispatch, getState) => { | 
        
        
           | 
                  const { counter } = getState(); | 
        
        
           | 
                  const newValue = counter / value; | 
        
        
           | 
          
 | 
        
        
           | 
                  return new Promise((resolve, reject) => { | 
        
        
           | 
                      resolve(newValue); | 
        
        
           | 
                  }).then((data) => { | 
        
        
           | 
                      dispatch(receiveUpdate(data)); | 
        
        
           | 
                  }); | 
        
        
           | 
              } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          // Commands | 
        
        
           | 
          class Command { | 
        
        
           | 
            execute() { | 
        
        
           | 
              throw new Error('Not Implemented'); | 
        
        
           | 
            } | 
        
        
           | 
          
 | 
        
        
           | 
            undo() { | 
        
        
           | 
              throw new Error('Not Implemented'); | 
        
        
           | 
            } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          class AddCommand extends Command { | 
        
        
           | 
              constructor(value) { | 
        
        
           | 
                  super(); | 
        
        
           | 
                  this.value = value; | 
        
        
           | 
              } | 
        
        
           | 
          
 | 
        
        
           | 
              execute() { | 
        
        
           | 
                  return add(this.value); | 
        
        
           | 
              } | 
        
        
           | 
          
 | 
        
        
           | 
              undo() { | 
        
        
           | 
                  return sub(this.value); | 
        
        
           | 
              } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          class SubCommand extends Command { | 
        
        
           | 
              constructor(value) { | 
        
        
           | 
                  super(); | 
        
        
           | 
                  this.value = value; | 
        
        
           | 
              } | 
        
        
           | 
          
 | 
        
        
           | 
              execute() { | 
        
        
           | 
                  return sub(this.value); | 
        
        
           | 
              } | 
        
        
           | 
          
 | 
        
        
           | 
              undo() { | 
        
        
           | 
                  return add(this.value); | 
        
        
           | 
              } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          class MulCommand extends Command { | 
        
        
           | 
              constructor(value) { | 
        
        
           | 
                  super(); | 
        
        
           | 
                  this.value = value; | 
        
        
           | 
              } | 
        
        
           | 
          
 | 
        
        
           | 
              execute() { | 
        
        
           | 
                  return mul(this.value); | 
        
        
           | 
              } | 
        
        
           | 
          
 | 
        
        
           | 
              undo() { | 
        
        
           | 
                  return div(this.value); | 
        
        
           | 
              } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          class DivCommand extends Command { | 
        
        
           | 
              constructor(value) { | 
        
        
           | 
                  super(); | 
        
        
           | 
                  this.value = value; | 
        
        
           | 
              } | 
        
        
           | 
          
 | 
        
        
           | 
              execute() { | 
        
        
           | 
                  return div(this.value); | 
        
        
           | 
              } | 
        
        
           | 
          
 | 
        
        
           | 
              undo() { | 
        
        
           | 
                  return mul(this.value); | 
        
        
           | 
              } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          
 | 
        
        
           | 
          // Middleware | 
        
        
           | 
          let commands = []; | 
        
        
           | 
          function undoMiddleware({ dispatch, getState }) { | 
        
        
           | 
            return function (next) { | 
        
        
           | 
              return function (action) { | 
        
        
           | 
                if (action instanceof Command) { | 
        
        
           | 
                  // Call the command | 
        
        
           | 
                  const promise = action.execute(action.value); | 
        
        
           | 
                  commands.push(action); | 
        
        
           | 
                  return promise(dispatch, getState); | 
        
        
           | 
                } else { | 
        
        
           | 
                  if (action.type === UNDO) { | 
        
        
           | 
                      const command = commands.pop(); | 
        
        
           | 
                      const promise = command.undo(command.value); | 
        
        
           | 
                      return promise(dispatch, getState); | 
        
        
           | 
                  } else { | 
        
        
           | 
                      return next(action); | 
        
        
           | 
                  } | 
        
        
           | 
                } | 
        
        
           | 
              }; | 
        
        
           | 
            }; | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          // Reducer | 
        
        
           | 
          function counterReducer(state=0, action) { | 
        
        
           | 
              switch (action.type) { | 
        
        
           | 
                  case RECEIVE_UPDATE: | 
        
        
           | 
                      return action.payload.counter; | 
        
        
           | 
                  default: | 
        
        
           | 
                      return state; | 
        
        
           | 
              } | 
        
        
           | 
          } | 
        
        
           | 
          
 | 
        
        
           | 
          const appReducer = combineReducers({ | 
        
        
           | 
            counter: counterReducer | 
        
        
           | 
          }); | 
        
        
           | 
          
 | 
        
        
           | 
          
 | 
        
        
           | 
          // Store | 
        
        
           | 
          const createStoreWithMiddleware = applyMiddleware( | 
        
        
           | 
            undoMiddleware, | 
        
        
           | 
          )(createStore); | 
        
        
           | 
          
 | 
        
        
           | 
          
 | 
        
        
           | 
          // App | 
        
        
           | 
          const store = createStoreWithMiddleware(appReducer); | 
        
        
           | 
          
 | 
        
        
           | 
          store.subscribe(() => { | 
        
        
           | 
            let state = store.getState(); | 
        
        
           | 
            console.log('-----------------------------------'); | 
        
        
           | 
            console.log('state', state); | 
        
        
           | 
            console.log('commands', commands); | 
        
        
           | 
            console.log(''); | 
        
        
           | 
          }); | 
        
        
           | 
          
 | 
        
        
           | 
          store.dispatch(new AddCommand(10)) | 
        
        
           | 
              .then(() => store.dispatch(new SubCommand(2))) | 
        
        
           | 
              .then(() => store.dispatch(new AddCommand(5))) | 
        
        
           | 
              .then(() => store.dispatch(undo())) | 
        
        
           | 
              .then(() => store.dispatch(undo())) | 
        
        
           | 
              .then(() => store.dispatch(new MulCommand(4))) | 
        
        
           | 
              .then(() => store.dispatch(new DivCommand(2))) | 
        
        
           | 
              .then(() => store.dispatch(undo())) |