redux-live-undo
A generic, high-order reducer that allows you to transparently add undo/redo functionality on top of other redux reducers. redux-live-undo allows state to be updated as users type without creating undo steps for every state change.
Inspired by the omnidan's work on redux-undo, redux-live-undo provides a simpler interface with admittedly less options but more flexibility. Most importantly, redux-live-undo has the concept of "history checkpoints" that allow you to dispatch actions without making every change to the state a step in the undo history.
For example, this allows you live updating of UI state (such as in a text input) without creating an undo step for every single keypress.
redux-live-undo also allows you to track entire sections of your application state as a single history so undo/redo actions can be synced across various types of data.
Brief Overview
redux-live-undo tracks a history of state of its sub-reducers. The state returned by redux-live-undo has this shape:
- present: the current state of the sub-reducers.
- past: an array of past state checkpoints, ordered from oldest to newest.
- future: an array of future state checkpoints, order from newest to oldest. Only populated after an undo or redo.
Note: New history checkpoints are only recorded when the action dispatched has the undoableHistoryCheckpoint
property set to true
and changeDetector
returns true.
Reducers can optionally provide a changeDetector
function to override what changes are allowed to be checkpointable.
Action flags
Flags on actions can affect the behavior of the undo history state:
undoableHistoryCheckpoint
: Marks when the next state should be considered a checkpoint in the undo historyundoableIrreversibleCheckpoint
: Marks when the next state should clear any undo history. Typically used if related state in another system (eg. a backend service) cannot be reversed.
Example Usage
Given a simple reducer that tracks the state of a string:
const TEXT_UPDATE = 'TEXT_UPDATE'; { }
Use redux-live-undo to add reducer functionality:
; const rootReducer = ;
When passed to combinedReducers, you will have access to present, past, and future states:
; const store = ;store;// => {// past: [''],// present: {// string: ''// },// future: []// }
Dispatch an update as an input changes:
store; store;// => {// past: [''],// present: {// string: 'H'// },// future: []// }
Dispatch an update with a checkpoint when you want a snapshot to jump back to, for example, when the input blurs:
store; store;// => {// past: ['', 'Hi'],// present: {// string: 'Hi'// },// future: []// }
Undo to the last checkpoint:
; store; store;// => {// past: [''],// present: {// string: ''// },// future: ['Hi']// }
Redo the action:
; store; store;// => {// past: ['', 'Hi'],// present: {// string: 'Hi'// },// future: []// }
Credits
- @joshdover
- @tornstrom