npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

redux-undo-redo

v2.1.1

Published

enables undo-redo by defining opposite actions

Downloads

507

Readme

redux-undo-redo

This package takes a different approach about implementing undo-redo functionality. Instead of setting a reducer to be undoable, we'll define which actions are undoable and define a reverting action.

Pros:

  1. Easily support multiple changes that should be treated as a single undo step. (animation, drag drop)
  2. We don't need to save the state, instead we save actions which are usually smaller in size
  3. Easily implement "undo actions list" component because we have the list of actions
  4. No change to state structure

Cons:

  1. Harder implementation than just applying high-order-reducer
  2. Developers need to be aware they need to provide reverting actions to support undo (or is it a pro?)

Usage

add the undoHistoryReducer to your top state

import {combineReducers} from 'redux'
import {undoHistoryReducer} from 'redux-undo-redo'

import {appReducer} from './app'
import {someOtherReducer} from './someOtherModule'

const rootReducer = combineReducers({
  app: appReducer,
  other: someOtherReducer,
  undoHistory: undoHistoryReducer
})

create undoMiddleware instance and attach it to the redux store

import {createUndoMiddleware} from 'redux-undo-redo'
import {setCurrentCounter, increment, decrement, setCounterValue} from './actions'
import {getCurrentCounter, getCurrentCounterValue} from './selectors'

const undoMiddleware = createUndoMiddleware({
  getViewState: getCurrentCounter,
  setViewState: setCurrentCounter,
  revertingActions: {
    'INCREMENT': (action) => decrement(),
    'DECREMENT': (action) => increment(),
    'SET_COUNTER_VALUE': {
      action: (action, {oldCounterValue}) => setCounterValue(oldCounterValue),
      createArgs: (state, action) => ({oldCounterValue: getCurrentCounterValue(state)})
    }
  }
})

createUndoMiddleware take a configuration object with the following fields:

revertingActions

This is a map between the action type and it's reverting action creator, the action creator gets the original action and should return the reverting action. If the the original action is not enough to create a reverting action you can provide createArgs that will result in an args argument for the reverting action:

{
  action: (action, args) => revertingActionCreator(action.something, args.somethingElse),
  createArgs: (state, action) => ({somethingElse: state.something})
}

the createArgs function runs before the action happens and collects information needed to revert the action. you get this as a second argument for the reverting action creator.

Note: "createArgs" was "meta" in previous versions of the library and changed to "createArgs" as it is clearer.

getViewState and setViewState

this to fields are optional getViewState is a selector like this: (state) => derivedState setViewState is an action creator that gets the result of getViewState as an argument: (viewState) => ({type: 'SET_VIEW_STATE', viewState}) if you define this selector and action the middleware will save the before and after view state of every undoable action. those would be used to dispatch setViewState before dispatching the reverting/original action to undo/redo. this is usful in cases where undoable actions depends on another part of the state.

after setting up the reducer and the middleware all you need to do is dispatch the provided actions like in redux-undo

import React from 'react'
import { actions as undoActions } from 'redux-undo-redo'
import { connect } from 'react-redux'

let UndoRedo = ({ canUndo, canRedo, onUndo, onRedo }) => (
  <p>
    <button onClick={onUndo} disabled={!canUndo}>
      Undo
    </button>
    <button onClick={onRedo} disabled={!canRedo}>
      Redo
    </button>
  </p>
)

const mapStateToProps = (state) => ({
  canUndo: state.undoHistory.undoQueue.length > 0,
  canRedo: state.undoHistory.redoQueue.length > 0
})

const mapDispatchToProps = ({
  onUndo: undoActions.undo,
  onRedo: undoActions.redo
})

UndoRedo = connect(
  mapStateToProps,
  mapDispatchToProps
)(UndoRedo)

here is a complete example

Limit the undo history

You can limit the number of undo operations stored by the reducer, by using createUndoHistoryReducer and passing in the limit:

import {combineReducers} from 'redux'
import {createUndoHistoryReducer} from 'redux-undo-redo'
import {appReducer} from './app'
import {someOtherReducer} from './someOtherModule'

const UNDO_HISTORY_LIMIT = 20

const rootReducer = combineReducers({
  app: appReducer,
  other: someOtherReducer,
  undoHistory: createUndoHistoryReducer(UNDO_HISTORY_LIMIT)
})