Skip to content

Instantly share code, notes, and snippets.

@gaearon
Created July 30, 2015 14:46
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gaearon/2b3ee315bb6a3b280b7c to your computer and use it in GitHub Desktop.
Save gaearon/2b3ee315bb6a3b280b7c to your computer and use it in GitHub Desktop.
Redux undo reducer factory example by @iclanzan
// Written by @iclanzan
// All credit goes to him!
// You create the reducer like this:
// var reducer = createTimelineReducer('someName', someReducer, ['foo', 'bar']);
// And then whenever an action of type `foo` or `bar` happens it calls `someReducer` and adds the result to the timeline.
// Then to undo/redo you trigger an action of type `someNameUndo`/`someNameRedo`.
var defaults = require('lodash/object/defaults');
var capitalize = require('lodash/string/capitalize');
var timelineActionTypes = ['add', 'seek', 'undo', 'redo'];
exports = module.exports = function (name, reducer, actionTypes, size) {
var initialState = {
timeline: [],
index: -1,
size: size || 1024
};
var handlers = timelineActionTypes.reduce(function (result, type) {
result[name + capitalize(type)] = exports[type];
return result;
}, {});
return function (state, action) {
if (state === undefined) {
state = initialState;
}
var actionType = action.type;
var handler = handlers[actionType];
if (handler) {
return handler(state, action.payload);
}
if (!~actionTypes.indexOf(actionType)) {
return state;
}
return add(state, reducer(state.current, action));
};
};
var add = exports.add = function (state, data) {
var current = state.current;
if (current == data) {
return state;
}
var nextState = {current: data};
var nextTimeline = nextState.timeline = state.timeline.slice(
state.index + 1 >= state.size ? 1 : 0,
state.index + 1
);
nextState.index = nextTimeline.push(data) - 1;
return defaults(nextState, state);
};
var seek = exports.seek = function (state, index) {
var timeline = state.timeline;
var maxIndex = timeline.length - 1;
if (index < 0) {
index = 0;
}
// Allow for -1 when timeline is empty.
if (index > maxIndex) {
index = maxIndex;
}
return index == state.index ? state : defaults({
index: index,
current: timeline[index]
}, state);
};
exports.undo = function (state, steps) {
return seek(state, state.index - (steps || 1));
};
exports.redo = function (state, steps) {
return seek(state, state.index + (steps || 1));
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment