With the latest update, I’ve enabled a full undo/redo system in WeTracker. This has been possible largely due to the use of ImmutableJS for the global state. I took a decision to use this to store global state after tinkering with React and Redux as a framework for WeTracker at the start. While I decided not to use these frameworks in the end, I learned a few lessons along the way that I liked and adopted, this was one.

The main complication was the decision not to store the song itself in the Immutable state. This took a while to remedy, but it’s worth it. It basically meant that the entire undo/redo system took less than a couple of hours to implement. Having all data stored in an Immutable object means that any change to the data results in a new version of the state, including all the song data (this might sound highly inefficient, but thanks to some clever referencing in the ImmutableJS implementation, it’s actually fine). All changes to the song are through a single function “set” on the state that merges in the given changes and creates a new version of the data. Now, before that happens, the current state is pushed onto a stack, and it’s child’s play from there to traverse back and forward through history.

As some operations can potentially result in multiple state changes, I added some simple functions to the system to allow me to mark the start and end of an atomic change, so for example, should adding a note to a pattern actually result in first adding the relevant row, then the track and then the event, it all looks like a single operation to the history, and can be undone/redone as one. Apart from that, it all worked straigh away.

The work to move the song into Immutable state has been big, and touched just about every part of the code, including the brittle player, so it may have introduced some regression, I’ll be monitoring that, if anyone sees (or more likely hears) any discrepancy in behaviour, please let me know.

Onwards and upwards…