On Github veggiemonk / slide-immutable-js
http://www.macwright.org/2015/05/18/practical-undo.html Original Article by Tom MacWright
The principles are:
var myself = { name: 'Tom' };
var someoneElse = myself;
myself.name = 'Thomas';
// both myself & someoneElse are now equal to { name: 'Thomas' }
// change arrays in place.
var numbers = [3, 2, 1];
var sorted = numbers.sort();
// both numbers & sorted are equal to [1, 2, 3]
// because calling .sort() sorts the array in-place
var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set('b', 50);
map1.get('b'); // 2
map2.get('b'); // 50
// shared structure
var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set('b', 2);
assert(map1 === map2); // no change
var map3 = map1.set('b', 50);
assert(map1 !== map3); // change
var myself = Immutable.Map({ name: 'Tom' });
// Instead of changing myself in-place
// like I would with vanilla JavaScript,
// Immutable provides methods that yield new modified objects.
var someoneElse = myself.set('name', 'Thomas');
// If you've dealt with this problem before,
// you might notice that there's another way
// of approaching this problem ==> by cloning objects:
var myself = { name: 'Tom' };
// clone myself, to ensure that changing someoneElse doesn't
// mutate it.
var someoneElse = JSON.parse(JSON.stringify(myself));
myself.name = 'Thomas';
var myself = { name: 'Tom' };
var someoneElse = JSON.parse(JSON.stringify(myself));
myself.name = 'Thomas';
Operations are functions that create new versions
// person is the data, and height is a property
// we want to change. this function creates a new
// version of person *without modifying the old one*
function changeHeight(person, height) {
    return person.set('height', height);
}
As simple as that: generally the start of the array is the first state, where there's no ability to go backwards, and the tip of the array is the present.
var historyIndex = 0;
var history = [Immutable.Map({ name: 'Tom' })];
    // history doesn't need to be immutable!
push a new version on the stack, and run an operation
function operation(fn) {
// eliminate the future
history = history.slice(0, historyIndex + 1);
// create a new version by applying an operation to the head
var newVersion = fn(history[historyIndex]);
    history.push(newVersion);
    historyIndex++;
}
function changeHeight(height) {
    operation(function(data) {
        return data.set('height', height);
    });
}
var hasUndo = historyIndex !== 0; var hasRedo = historyIndex !== history.length - 1;
Please change me!
Along with an operation, you'll add a little text snippet, like "Drew a circle" or "Changed a color".
function operation(fn, annotation) {
    // eliminate the future
    annotations = annotations.slice(0, historyIndex + 1);
    history = history.slice(0, historyIndex + 1);
    // create a new version by applying an operation to the head
    var newVersion = fn(history[historyIndex]);
    history.push(newVersion);
    annotations.push(annotation);
    historyIndex++;
}
// an operation that adds an annotation
function changeHeight(height) {
  operation(function(data) {
    return data.set('height', height);
  }, 'Changed the height');
}