Redux Deep Diff
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:
;// redux-deep-diff higher-order reducer;// `counter` state must be a diffable object// e.i. state.counter = { count: 1 }counter:
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: ...previousDiffsnext: ...futureDiffs
Each diff in the history is an array of changes. See deep-diff's differences documentation for more info on change records.
undo
& redo
actions
Dispatching Since redux-deep-diff
tracks changes to your state, you can undo & redo
the diffs in your history.
;store; // revert the previous diffstore; // apply the next diffstore; // revert the previous three diffsstore; // apply the next two diffs
clear
action
Dispatching ;store; // 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.
;const countSelector = countercount;const getCountHistory = ;// `state.counter` must countain a diff history leaf; //=> [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;let a = ;//=> [0, 1] `countSelector` was called twicestore;store;store;let b = ;//=> [0, 1, 2, 3, 2] `countSelector` was called three more times// true even for values of objects/arraysto;to;let c = ;//=> [0, 1, 2, 3, 2] `countSelector` was not called// when no changes are made, the same results are returnedto;// ...but a new array is returned when values do changetonot;
Configuration options
Default values for the supported options are listed below
Reducer options
key: 'diff' // key to store the state diffslimit: 0 // diff history limitundoType: '@@redux-deep-diff/UNDO' // custom undo action typeredoType: '@@redux-deep-diff/REDO' // custom redo action typejumpType: '@@redux-deep-diff/JUMP' // custom jump action typeclearType: '@@redux-deep-diff/CLEAR' // custom clear action typefalse // return true to skip diffing the state for this actioninitialState: prev: next: // initial diff history stateignoreInit: true // includes the first state when `false`false // (see below)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 key === 'nested';/* ...reducer setup... */todeeptodeep;todeep;todeep;
Deducer options
key: 'diff' // key to retrieve the state diffsnext: false // deduce from `diff.next` when `true` (future history)unique: false // skip equal results that immediately follow each otherindex: false // the index of a single state in the history to deducerange: 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.