JSPM

  • Created
  • Published
  • Downloads 78685
  • Score
    100M100P100Q162210F
  • License MIT

simple undo/redo functionality for redux state containers

Package Exports

  • redux-undo

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (redux-undo) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

redux undo/redo

NPM version (>=0.4) Build Status Dependencies https://gratipay.com/omnidan/

simple undo/redo functionality for redux state containers

Protip: You can use the redux-undo-boilerplate to quickly get started with redux-undo.

https://i.imgur.com/M2KR4uo.gif

Note: When upgrading from 0.3 to 0.4, use .present instead of .currentState.

Installation

npm install --save redux-undo

Making your reducers undoable

redux-undo is a reducer enhancer, it provides the undoable function, which takes an existing reducer and a configuration object and enhances your existing reducer with undo functionality.

Note: If you were accessing state.counter before, you have to access state.counter.present after wrapping your reducer with undoable.

To install, firstly import redux-undo:

// Redux utility functions
import { combineReducers } from 'redux';
// Redux Undo store enhancer
import undoable from 'redux-undo';

Then, add undoable to your reducer(s) like this:

combineReducers({
  counter: undoable(counter)
})

A configuration can be passed like this:

combineReducers({
  counter: undoable(counter, {
    limit: 10 // set a limit for the history
  })
})

Usage

Firstly, import the undo/redo action creators:

import { ActionCreators } from 'redux-undo';

Then, you can use store.dispatch() and the undo/redo action creators to perform undo/redo operations on your state:

store.dispatch(ActionCreators.undo()) // undo the last action
store.dispatch(ActionCreators.redo()) // redo the last action

Configuration

A configuration object can be passed to undoable() like this (values shown are default values):

undoable(reducer, {
  limit: false, // set to a number to turn on a limit for the history

  filter: () => true, // see `Filtering Actions` section

  undoType: ActionTypes.UNDO, // define a custom action type for this undo action
  redoType: ActionTypes.REDO, // define a custom action type for this redo action

  initialState: undefined, // initial state (e.g. for loading)
  initTypes: ['@@redux/INIT', '@@INIT'] // history will be (re)set upon init action type
  initialHistory: { // initial history (e.g. for loading)
    past: [],
    present: config.initialState,
    future: [],
  },

  debug: false, // set to `true` to turn on debugging
})

Filtering Actions

If you don't want to include every action in the undo/redo history, you can pass a function to undoable like this:

undoable(reducer, function filterActions(action, currentState, previousState) {
  return action.type === SOME_ACTION; // only add to history if action is SOME_ACTION
})

// or you could do...

undoable(reducer, function filterState(action, currentState, previousState) {
  return currentState !== previousState; // only add to history if state changed
})

Or you can use the distinctState, ifAction and excludeAction helpers, which should be imported like this:

import undoable, { distinctState, ifAction, excludeAction } from 'redux-undo';

Now you can use the helper, which is pretty simple:

undoable(reducer, { filter: ifAction(SOME_ACTION) })
undoable(reducer, { filter: excludeAction(SOME_ACTION) })

// or you could do...

undoable(reducer, { filter: distinctState() })

... they even support Arrays:

undoable(reducer, { filter: ifAction([SOME_ACTION, SOME_OTHER_ACTION]) })
undoable(reducer, { filter: excludeAction([SOME_ACTION, SOME_OTHER_ACTION]) })

History API

Wrapping your reducer with undoable makes the state look like this:

{
  present: {...currentStateHere...},
  history: {
    past: [...pastStatesHere...],
    present: {...currentStateHere...},
    future: [...futureStatesHere...]
  }
}

Which means you can access all past states (e.g. to show a history) like this: state.history.past

What is this magic? How does it work?

Have a read of the Implementing Undo History recipe in the Redux documents, which explains in detail how redux-undo works.

License

MIT, see LICENSE.md for more information.