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-deep-diff

v0.5.0

Published

Higher order reducer to deep diff redux states

Downloads

253

Readme

Redux Deep Diff

npm version Build Status

deep-diff for tracking structural differences between objects in redux state containers

Also with undo/redo functionality!

Installation

$ yarn add redux-deep-diff

API

redux-deep-diff is a reducer enhancer (higher-order reducer), it provides the diff function, which takes an existing reducer and a configuration object and enhances your existing reducer with deep-diff functionality.

Note: the deep-diff library calculates structural changes between two javascript objects. As such, your state must be a diffable object.

To use redux-deep-diff, first import it, then add the diff enhancer to your reducer(s) like so:

import { combineReducers } from 'redux';

// redux-deep-diff higher-order reducer
import diff from 'redux-deep-diff';

// `counter` state must be a diffable object
// e.i. state.counter = { count: 1 }
export default combineReducers({
  counter: diff(counter, /* [config] */)
})

To configure how diff enchances your reducers, see the possible configuration options below.

Diff API

Wrapping your reducer with diff adds a diff leaf to your state:

{
  diff: {
    prev: [...previousDiffs],
    next: [...futureDiffs]
  }
}

Each diff in the history is an array of changes. See deep-diff's differences documentation for more info on change records.

Dispatching undo & redo actions

Since redux-deep-diff tracks changes to your state, you can undo & redo the diffs in your history.

import { undo, redo, jump } from 'redux-deep-diff';

store.dispatch(undo()); // revert the previous diff
store.dispatch(redo()); // apply the next diff

store.dispatch(jump(-3)); // revert the previous three diffs
store.dispatch(jump(2)); // apply the next two diffs

Dispatching clear action

import { clear } from 'redux-deep-diff';

store.dispatch(clear()); // clear the history

Deducing state history

To access previous values of the state, redux-deep-diff has a concept of deducers which are similar to selectors in that you create a deducer and use it to calculate ("deduce") part of your state's history.

import { createDeducer } from 'redux-deep-diff';

const countSelector = (counter) => counter.count;
const getCountHistory = createDeducer(countSelector, /* [config] */);

// `state.counter` must countain a diff history leaf
getCountHistory(state.counter); //=> [0, 1, 2, 3, 2, ...]

To configure how a deducer determines which diffs to deduce, see the possible configuration options below.

Similarly to selectors, deducers are also memoized and not only memoize their return value, but the return value of the selector for each set of changes in the diff history. When the diff history changes, deducers will only call the selector with states it hasn't yet deduced.

store.dispatch(increment());
let a = getCountHistory(store.getState());
//=> [0, 1] `countSelector` was called twice

store.dispatch(increment());
store.dispatch(increment());
store.dispatch(decrement());

let b = getCountHistory(store.getState());
//=> [0, 1, 2, 3, 2] `countSelector` was called three more times

// true even for values of objects/arrays
expect(a[0]).to.equal(b[0]);
expect(a[1]).to.equal(b[1]);

let c = getCountHistory(store.getState());
//=> [0, 1, 2, 3, 2] `countSelector` was not called

// when no changes are made, the same results are returned
expect(b).to.equal(c);
// ...but a new array is returned when values do change
expect(a).to.not.equal(b);

Configuration options

Default values for the supported options are listed below

Reducer options

{
  key: 'diff',                          // key to store the state diffs
  limit: 0,                             // diff history limit
  undoType: '@@redux-deep-diff/UNDO',   // custom undo action type
  redoType: '@@redux-deep-diff/REDO',   // custom redo action type
  jumpType: '@@redux-deep-diff/JUMP',   // custom jump action type
  clearType: '@@redux-deep-diff/CLEAR',   // custom clear action type
  skipAction: (action) => false,        // return true to skip diffing the state for this action
  initialState: { prev: [], next: [] }, // initial diff history state
  ignoreInit: true,                     // includes the first state when `false`
  prefilter: (path, key) => false,      // (see below)
  flatten: (path, key) => false         // (see below)
}

prefilter(path, key)

See deep-diff's prefilter argument.

flatten(path, key)

Similar to prefilter, flatten should return a truthy value for any path-key combination that should be flattened. That is: deep-diff will not do any further analysis on the object-property path, but the resulting value of the change will be the value located at the flattened path.

let flatten = (path, key) => key === 'nested';

/* ...reducer setup... */

expect(state.nested).to.deep.equal({ some: 'property' })
expect(change.path).to.deep.equal(['nested']);
expect(change.lhs).to.deep.equal({ some: 'prev-property' });
expect(change.rhs).to.deep.equal({ some: 'property' });

Deducer options

{
  key: 'diff',   // key to retrieve the state diffs
  next: false,   // deduce from `diff.next` when `true` (future history)
  unique: false, // skip equal results that immediately follow each other
  index: false,  // the index of a single state in the history to deduce
  range: false,  // a range of history states to deduce - `[lower, upper]`
  limit: false   // limit the deducer to a specified length
}

Important: index, range, and limit may not be used in conjuction. If they are used together, precedence will be taken in that order and a warning will be logged.

License

MIT, see LICENSE.md for more info.