8

How do I make undo / redo using Vuex? I am working on a pretty complex app and Vue dev tools helped me a lot to switch between state, so I want that feature on my app. How can I achieve this?

exclsr
  • 3,329
  • 23
  • 27

2 Answers2

21

I've implemented undo-redo as follows:

1) create a plugin for vuex

const undoRedoPlugin = (store) => {
  // initialize and save the starting stage
  undoRedoHistory.init(store);
  let firstState = cloneDeep(store.state);
  undoRedoHistory.addState(firstState);

  store.subscribe((mutation, state) => {
    // is called AFTER every mutation
    undoRedoHistory.addState(cloneDeep(state));
  });
}

2) use that plugin

new Vuex.Store({
... 
  plugins: [undoRedoPlugin]
});

3) save a history of the states in undoRedoHistory

class UndoRedoHistory {
  store;
  history = [];
  currentIndex = -1;

  init(store) {
    this.store = store;
  }

  addState(state) {
    // may be we have to remove redo steps
    if (this.currentIndex + 1 < this.history.length) {
      this.history.splice(this.currentIndex + 1);
    }
    this.history.push(state);
    this.currentIndex++;
  }

  undo() {
    const prevState = this.history[this.currentIndex - 1];
    // take a copy of the history state
    // because it would be changed during store mutations
    // what would corrupt the undo-redo-history
    // (same on redo)
    this.store.replaceState(cloneDeep(prevState));
    this.currentIndex--;
  }

  redo() {
    const nextState = this.history[this.currentIndex + 1];
    this.store.replaceState(cloneDeep(nextState));
    this.currentIndex++;
  }
}

const undoRedoHistory = new UndoRedoHistory();

4) use it

undoRedoHistory.undo();
...
undoRedoHistory.redo();

If your state is not huge in size than cloning that states is a good approach.

Reinhard
  • 1,516
  • 1
  • 18
  • 25
  • 1
    using [lzutf8](https://www.npmjs.com/package/lzutf8) to compress the states while keeping them in the history can save us some memory storage. – Mohammad Hassany Dec 21 '21 at 15:37
7

See: https://vuex.vuejs.org/en/api.html

You can easely use subscribe(handler: Function) to register a function that keeps all the states you want from a given Store in an array.

Then you can use any of the saved state in that array by giving them as argument to replaceState(state: Object).

FitzFish
  • 8,557
  • 2
  • 30
  • 39