Question

I'm developing an experimental application for work that requires fully persistent undos and redos. I decided to use react/redux/immutable JS tech stack to solve this problem because I saw it worked out nicely.

I'm actually really happy with the simplicity of the solution but my issue with this decision is that this architecture and tech-stack deviates from what the team currently uses which is an angular/polymer/vue style two-way data-binding architecture.

So in an effort to remain open-minded:

Is undo and redo possible/feasible with two-way data binding when compared uni-directional data flow and fully persistent data structures?

Why or why not? How do you achieve fully-persistent undos/redos with two-way data binding?

Edit:

From my comment below:

I am concerned with recording any change to the data model so the user can undo those. I have personally found success in doing so using immutable persistent data structures with 'uni-directional data flow' because I know where changes come in and where to save previous models. I can then emit a previous model and have react re-render when an undo action occurs. Now what I want to know is how to implement undos and redos that can track any change and undo it using two-way data binding architecture instead. I'm open to any solution that solves this problem using two-way data binding

Was it helpful?

Solution

Typically you implement an undo/redo feature using a command history. Your UI generates commands with execute and unexecute actions, and the underlying model is changed by playing the command's execute action on it. Then navigating the undo/redo history is just performing execute or unexecute actions on the "current" command and updating the history's pointer to the appropriate command. This means that you do not store complete models for previous entries, just the command that produced them from the previous state.

In one-way data-binding the appropriate way to implement is by having the UI generate the commands directly and then executing them on the underlying model layer. In a two-way data binding it can be a bit more awkward, but you can still generate the command history from events on the model layer, and then do some bookkeeping in those event handlers to prevent them from firing when you are navigating the command history and (un)executing the commands to update the model.

Either way, what you do not need to do is store previous models in their entirety. You only need the delta between states, and that is represented best by the command that generated the change.

Licensed under: CC-BY-SA with attribution
scroll top