foxhound87
5/25/2016 - 3:17 PM

mobx_timetravel.js

import mobx from 'mobx'

// Very naive concept of performant mobx undo

var history = []

// Let's say mobx have observeAll function acting as observe, but on all mobx observable entities.
// `change` object would need to have `observedType` key.
// It would also observe primitive observables.
mobx.observeAll((change) => {
  history.push(change)
})

function getHistoryPosition() {
  return history.length
}

function backToHistoryPosition(index) {
  while(history.length > index) {
    const change = history.pop()
    undoChange(change)
  }
}

function undoChange(change) {
  switch(change.observedType) {
    case 'map':
      switch (change.type) {
        case 'add':
          change.object.delete(change.name)
          return
        case 'delete':
          change.object.set(change.name, change.oldValue)
          return
        case 'update':
          change.object.set(change.name, change.oldValue)
          return
      }
    case 'array':
      // similar to map case
    case 'primitive':
      // similar to map case
    case 'object':
      // similar to map case
      // ? here we would need mobx.unextendObservable to reverse mobx.extendObservable ?
    default:
      invariant( false, 'change object should be like object returned by `mobx.observe` with added key `observedType`')
  }
}

// Then it can be used like:

const state = myLib.createMyFancyStore()

function actionOnButtonClick() {
  let historyBeforeUpdate = getHistoryPosition()

  state.optimisticallyUpdate()

  state.sendUpdateToServer((err) => {
    if(err) {
      // If action didn't come through we can roll it back
      backToHistoryPosition(historyBeforeUpdate)
    }
  })
}