Implementing Undo-Redo in React components using Flux and immutable data-structures

Facebook talked about their Flux workflow recently, which uses a one-way data flow in building apps. Brandon Tilley released a simple implementation last week with Fluxxor. He has written a nice description of the overall process here. I have built over the Fluxxor Todo example to implement the Undo functionality myself. You can view a demo of the implementation here.

I tried to extend the Flux store in a way similar to David Nolen with Om, except, I used the immutable structures at the store level rather than on the React components themselves. Extending the store was rather trivial. In the initialize call of the store, I added

 this.todoState = "BLANK_STATE";
 this.undoStates = Mori.list("BLANK_STATE");
 this.redoStates = Mori.list();
 this.canUndo = false;
 this.canRedo = false;

I have used strings to store each state as they are immutable in JavaScript. undoStates and redoStates are an implementation of a List from functional programming.

Further, after each update, I add

  this.todosState = JSON.stringify(this.todos);
  this.undoStates = Mori.conj(this.undoStates, this.todosState);
  this.redoStates = Mori.list();
  this.canUndo = true;
  this.canRedo = false;

We also need handlers for undo and redo

  undo: function() {
    this.redoStates = Mori.conj(this.redoStates, Mori.first(this.undoStates));
    this.undoStates = Mori.drop(1, this.undoStates);
    this.todosState = Mori.first(this.undoStates);
    this.canUndo = Mori.count(this.undoStates) > 1;
    this.canRedo = true;
    if(Mori.count(this.undoStates) > 1) {
      this.todos = JSON.parse(this.todosState);
    }
    else this.todos = [];
    this.emit("change");
  },

  redo: function() {
    this.undoStates = Mori.conj(this.undoStates, Mori.first(this.redoStates));
    this.todosState = Mori.first(this.redoStates);
    this.redoStates = Mori.drop(1, this.redoStates);
    this.canRedo = !Mori.is_empty(this.redoStates);
    this.canUndo = true;
    this.todos = JSON.parse(this.todosState);
    this.emit("change");
  }

Finally, todos, canUndo and canRedo are pushed to the React component state. The corresponding action handlers are also written in.

I have implemented the Undo/Redo for a single store here. To implement the same for multiple stores, we just modify the dispatcher to keep track of what all stores are modified at each step and fire the undo and redo events for each store. We can also implement the functionality for a component that talks with a store trivially too.

You can view the code on Github. If you have any feedback, do write to me at [email protected]