Releases: charkour/zundo
v2.2.0 - TypeScript Bundling Updates
Updates
Nothing much was changed other than updating dependencies, fixing a bug in a test, and adding automated workflows.
What's Changed
- switch to happy-dom and bump deps by @charkour in #162
- make update about reactive data by @charkour in #165
- setup jsr config by @charkour in #164
- Revert "setup jsr config" by @charkour in #167
- fix spying on wrong function by @charkour in #169
- Add Test Matrix by @charkour in #168
- chore: update deps by @charkour in #173
- update matrix and version by @charkour in #174
- add vitest coverage by @charkour in #175
- some updates by @charkour in #178
- bump deps and remove v5 support by @charkour in #186
- Add input example and update types by @charkour in #185
Full Changelog: v2.1.0...v2.2.0
v2.1.0 - Update Handle Set arguments
Release notes
- fixed a bug for complex cases of zundo and update
handleSet
arguments to includecurrentState
anddeltaState
(#139) in #149- π¨ Warning: This changes behavior that previously was a bug. If you relied on this behavior, there may be a breaking change.
- updated peer dependency to test with zustand v4.5
What's Changed
- Add more examples by @charkour in #148
- update to 700 B by @charkour in #150
- handleSet refactor option 3 by @pstrassmann in #149
- zustand v4.5.0 and bump deps by @charkour in #155
Full Changelog: v2.0.3...v2.1.0
v2.0.3 - TS Fix and Docs
Release notes
- @pstrassmann fixed a TypeScript bug and spent a lot of time updating the docs! π
What's Changed
- fix equality fn type and update docs by @pstrassmann in #144
- fix TS usage on docs, small formatting by @charkour in #146
- bump deps by @charkour in #147
New Contributors
- @pstrassmann made their first contribution in #144
Full Changelog: v2.0.2...v2.0.3
v2.0.2 - Actually support ESM and CJS
Release notes
v2.0.1 attempted to support ESM and CJS, but it instead changed the module support from CJS to ESM which is a potentially breaking change based on the user's JS runtime specification. This change fixes the package.json
file to support ESM or CJS, depending on what's required by the user's runtime.
What's Changed
Full Changelog: v2.0.1...v2.0.2
v2.0.1 - CJS (and ESM) and Deno
v2.0.1 Release Notes
- π¨ Potentially breaking: this release introduces a potentially breaking change, the default output switched from CJS to ESM. Please use version v2.0.2 to avoid this potentially breaking change.
- zundo is available via deno.land/x/zundo
- zundo is available in
cjs
in addition toesm
now
What's Changed
- Bump vite from 4.4.9 to 4.4.12 by @dependabot in #133
- Bump next from 13.4.19 to 13.5.1 by @dependabot in #135
- bump deps, and now export cjs and esm by @charkour in #136
- create separate size package to reduce shipped package.json size by @charkour in #137
New Contributors
- @dependabot made their first contribution in #133
Full Changelog: v2.0.0...v2.0.1
v2.0.0 - Smaller and more flexible π
v2.0.0 is a complete rewrite of zundo. It is smaller and more flexible. It also has a smaller bundle size and allows you to opt into specific performance trade-offs. The API has changed slightly. See the API section for more details. Below is a summary of the changes as well as steps to migrate from v1 to v2.
v2 introduces 8 new middleware options and is only ~800 bytes where v1 was a few KBs.
Community
zundo
is used by several projects and teams including Stability AI, Yext, KaotoIO, and NutSH.ai.
If this library is useful to you, please consider sponsoring the project. Thank you!
PRs are welcome! pnpm is used as a package manager. Run pnpm install
to install local dependencies. Thank you for contributing!
Breaking Changes
Middleware Option Changes
include
andexclude
options are now handled by thepartialize
option.allowUnchanged
option is now handled by theequality
option. By default, all state changes are tracked. In v1, we bundledlodash.isequal
to handle equality checks. In v2, you are able to use any function.historyDepthLimit
option has been renamed tolimit
.coolOffDurationMs
option is now handled by thehandleSet
option by wrapping the setter function with a throttle or debounce function.
Import changes
- The middleware is called
temporal
rather thanundoMiddleware
.
New Features
New Options
partialize
option to omit or include specific fields. By default, the entire state object is tracked.limit
option to limit the number of previous and future states stored in history.equality
option to use a custom equality function to determine when a state change should be tracked. By default, all state changes are tracked.diff
option to store state delta rather than full object.onSave
option to call a function when the temporal store is updated.handleSet
option to throttle or debounce state changes.pastStates
andfutureStates
options to initialize the temporal store with past and future states.wrapTemporal
option to wrap the temporal store with middleware. Thetemporal
store is a vanilla zustand store.
New temporal.getState()
API
undo
,redo
, andclear
functions are now always defined. They can no longer beundefined
.undo()
andredo()
functions now accept an optionalsteps
parameter to go back or forward multiple states at once.isTracking
flag, andpause
, andresume
functions are now available on the temporal store.setOnSave
function is now available on the temporal store to change theonSave
behavior after the store has been created.
Migration Steps
- Update zustand to v4.3.0 or higher
- Update zundo to v2.0.0 or higher
- Update your store to use the new API
- Update imports
- import { undoMiddleware } from 'zundo';
+ import { temporal } from 'zundo';
- If you're using
include
orexclude
, use the newpartialize
option
// v1.6.0
// Only field1 and field2 will be tracked
const useStoreA = create<StoreState>(
undoMiddleware(
set => ({ ... }),
{ include: ['field1', 'field2'] }
)
);
// Everything besides field1 and field2 will be tracked
const useStoreB = create<StoreState>(
undoMiddleware(
set => ({ ... }),
{ exclude: ['field1', 'field2'] }
)
);
// v2.0.0
// Only field1 and field2 will be tracked
const useStoreA = create<StoreState>(
temporal(
(set) => ({
// your store fields
}),
{
partialize: (state) => {
const { field1, field2, ...rest } = state;
return { field1, field2 };
},
},
),
);
// Everything besides field1 and field2 will be tracked
const useStoreB = create<StoreState>(
temporal(
(set) => ({
// your store fields
}),
{
partialize: (state) => {
const { field1, field2, ...rest } = state;
return rest;
},
},
),
);
- If you're using
allowUnchanged
, use the newequality
option
// v1.6.0
// Use an existing `allowUnchanged` option
const useStore = create<StoreState>(
undoMiddleware(
set => ({ ... }),
{ allowUnchanged: true }
)
);
// v2.0.0
// Use an existing equality function
import { shallow } from 'zustand/shallow'; // or use `lodash.isequal` or any other equality function
// Use an existing equality function
const useStoreA = create<StoreState>(
temporal(
(set) => ({
// your store fields
}),
{ equality: shallow },
),
);
- If you're using
historyDepthLimit
, use the newlimit
option
// v1.6.0
// Use an existing `historyDepthLimit` option
const useStore = create<StoreState>(
undoMiddleware(
set => ({ ... }),
{ historyDepthLimit: 100 }
)
);
// v2.0.0
// Use `limit` option
const useStore = create<StoreState>(
temporal(
(set) => ({
// your store fields
}),
{ limit: 100 },
),
);
- If you're using
coolOffDurationMs
, use the newhandleSet
option
// v1.6.0
// Use an existing `coolOffDurationMs` option
const useStore = create<StoreState>(
undoMiddleware(
set => ({ ... }),
{ coolOfDurationMs: 1000 }
)
);
// v2.0.0
// Use `handleSet` option
const withTemporal = temporal<MyState>(
(set) => ({
// your store fields
}),
{
handleSet: (handleSet) =>
throttle<typeof handleSet>((state) => {
console.info('handleSet called');
handleSet(state);
}, 1000),
},
);
What's Changed
- reworked types, methods now on useStore by @funwithtriangles in #44
- zundo v2 by @charkour in #43
- bump peer by @charkour in #51
- cleanup for v2 by @charkour in #52
- small tweaks to types and docs by @charkour in #53
- bump dev deps by @charkour in #54
- add subscribeWithSelector demo, react docs by @charkour in #56
- create new arrays by @charkour in #58
- fix step bug not reversing order by @charkour in #60
- fix: support zustand@4.3.x by @BrunnerLivio in #63
- cleanup for next release by @charkour in #65
- add modified setState by @charkour in #68
- export types by @charkour in #72
- bump deps by @charkour in #77
- add github action by @BrunnerLivio in #64
- shave some bytes by @charkour in #78
- shave more bytes by @charkour in #79
- restructure __internals to shave bytes; add react tests by @charkour in #82
- switch config for better types and save bytes by @charkour in #83
- init states by @charkour in #81
- Add middleware to temporal store by @charkour in #86
- drop node 14 support by @charkour in #93
- shave 9 bytes by @charkour in #94
- rename internal types by @charkour in #95
- remove objects. save 19 bytes by @charkour in #96
- simplify types, refactor _handleSet heuristics by @charkour in #97
- move immer to root to fix broken examples from 8608dd9 by @charkour in #99
- simplify repo by @charkour in #100
- update tsconfig for examples by @charkour in #102
- fix async test by @charkour in #103
- bump deps by @charkour in #104
- shave 2 bytes by @charkour in #108
- add while by @charkour in #107
- use ...args by @charkour in #110
- Copy past and future states only if necessary by @KevinMusgrave in #112
- switch to isTracking flag by @charkour in #115
- switch to temporal state creator by @charkour in #116
- while loop --> array methods by @charkour in #117
- shave 2 bytes by updating deps by @charkour in #120
- bump deps by @charkour in #122
- feat: add
diff
option to track state deltas by @charkour in #123 - add diffing during undo and redo by @charkour in #125
- switch to json or mjs by @charkour in #126
- first draft by @charkour in #127
New Contributors
- @BrunnerLivio made their first contribution in #63
- @KevinMusgrave made their first contribution in #112
Full Changelog: v1.6.0...v2.0.0
v2.0.0-beta.25 - track deltas
Allows for runtime perf which results in a larger bundle size. More space efficient but less time efficient.
819 B --> 848 B (3.54% increase)
Usage
Store state delta rather than full object
diff?: (pastState: Partial<PartialTState>, currentState: Partial<PartialTState>) => Partial<PartialTState> | null
For performance reasons, you may want to store the state delta rather than the complete (potentially partialized) state object. This can be done by passing a diff
function. The diff
function should return an object that represents the difference between the past and current state. By default, the full state object is stored.
If diff
returns null
, the state change will not be tracked. This is helpful for a conditionally storing past states or if you have a doNothing
action that does not change the state.
You can write your own or use something like microdiff
, just-diff
, or deep-object-diff
.
const useStore = create<StoreState>(
temporal(
(set) => ({
// your store fields
}),
{
diff: (pastState, currentState) => {
const myDiff = diff(currentState, pastState);
const newStateFromDiff = myDiff.reduce(
(acc, difference) => {
type Key = keyof typeof currentState;
if (difference.type === 'CHANGE') {
const pathAsString = difference.path.join('.') as Key;
acc[pathAsString] = difference.value;
}
return acc;
},
{} as Partial<typeof currentState>,
);
return isEmpty(newStateFromDiff) ? null : newStateFromDiff;
},
},
),
);
What's Changed
- bump deps by @charkour in #122
- feat: add
diff
option to track state deltas by @charkour in #123 - add diffing during undo and redo by @charkour in #125
Full Changelog: v2.0.0-beta.24...v2.0.0-beta.25
v2.0.0-beta.24 - Update deps, smaller build
v2.0.0-beta.23 - Use Array methods
833 B --> 821 B
Switch from using a while loop to using array methods.
This allows us to shrink the bundle size and to only perform array copies if we are actually going to commit something to the store.
What's Changed
Full Changelog: v2.0.0-beta.22...v2.0.0-beta.23