Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,9 @@ const useStoreWithUndo = create<StoreState>()(
}),
{
handleSet: (handleSet) =>
throttle<typeof handleSet>((state) => {
throttle<typeof handleSet>((...args) => {
console.info('handleSet called');
handleSet(state);
handleSet(...args);
}, 1000),
},
),
Expand Down Expand Up @@ -559,6 +559,40 @@ PRs are welcome! [pnpm](https://pnpm.io/) is used as a package manager. Run `pnp
- [canUndo, canRedo, undoDepth, redoDepth](https://codesandbox.io/s/zundo-canundo-and-undodepth-l6jclx?file=/src/App.tsx:572-731)
- [with deep equal](https://codesandbox.io/p/sandbox/zundo-deep-equal-qg69lj)

## Migrate from v2 to v3

<details>
<summary>Click to expand</summary>

## v3.0.0

### Breaking Changes

#### `wrapTemporal` behavior changes

If you were previously modifying the store with returning a new `set` in the `config`, you'll need to overwrite the `store.setState` function.

```tsx
// v2.0.0

// v3.0.0

```

#### `handleSet` argument

The second parameter of `handleSet` has been removed. If you were previously using `replace` in `handleSet`, you'll need to modify your `handleSet` function.

```tsx
// v2.0.0


// v3.0.0

```

</details>

## Migrate from v1 to v2

<details>
Expand Down
4 changes: 2 additions & 2 deletions examples/web/pages/chained.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ interface MyState {
// }),
// {
// handleSet: (handleSet) =>
// throttle<typeof handleSet>((state) => {
// throttle<typeof handleSet>((...args) => {
// console.error('handleSet called');
// handleSet(state);
// handleSet(...args);
// }, 1000),
// },
// );
Expand Down
4 changes: 2 additions & 2 deletions examples/web/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ const withZundo = temporal<MyState>(
}),
{
handleSet: (handleSet) =>
throttle<typeof handleSet>((state) => {
throttle<typeof handleSet>((...args) => {
console.info('handleSet called');
handleSet(state);
handleSet(...args);
}, 1000),
},
);
Expand Down
4 changes: 2 additions & 2 deletions examples/web/pages/options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ const useMyStore = create<MyState>()(
{
equality: deepEqual,
handleSet: (handleSet) =>
throttle<typeof handleSet>((state) => {
handleSet(state);
throttle<typeof handleSet>((...args) => {
handleSet(...args);
}, 500),
partialize: (state): HistoryTrackedState => {
const { untrackedValue, ...trackedValues } = state;
Expand Down
62 changes: 27 additions & 35 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
_TemporalState,
Write,
ZundoOptions,
HandleSet,
} from './types';

type Zundo = <
Expand Down Expand Up @@ -50,40 +51,34 @@ export const temporal = (<TState>(
temporalStateCreator(set, get, options),
);

const handleSet = (
pastState: TState,
// `replace` will likely be deprecated and removed in the future
replace: boolean | undefined,
currentState: TState,
deltaState?: Partial<TState>,
const handleSet: HandleSet<TState> = (
pastState,
currentState,
deltaState,
) => {
if (store.temporal.getState().isTracking) {
// This naively assumes that only one new state can be added at a time
if (
options?.limit &&
store.temporal.getState().pastStates.length >= options?.limit
) {
store.temporal.getState().pastStates.shift();
}

(store.temporal.getState() as _TemporalState<TState>)._onSave?.(
pastState,
currentState,
);
store.temporal.setState({
pastStates: store.temporal
.getState()
.pastStates.concat(deltaState || pastState),
futureStates: [],
});
// This naively assumes that only one new state can be added at a time
if (
options?.limit &&
store.temporal.getState().pastStates.length >= options?.limit
) {
store.temporal.getState().pastStates.shift();
}

(store.temporal.getState() as _TemporalState<TState>)._onSave?.(
pastState,
currentState,
);
store.temporal.setState({
pastStates: store.temporal
.getState()
.pastStates.concat(deltaState || pastState),
futureStates: [],
});
};

const curriedHandleSet =
options?.handleSet?.(handleSet as StoreApi<TState>['setState']) ||
handleSet;
const curriedHandleSet = options?.handleSet?.(handleSet) || handleSet;

const temporalHandleSet = (pastState: TState) => {
const temporalHandleSet = (pastState: TState): void => {
if (!store.temporal.getState().isTracking) return;

const currentState = options?.partialize?.(get()) || get();
Expand All @@ -92,14 +87,11 @@ export const temporal = (<TState>(
// Don't call handleSet if state hasn't changed, as determined by diff fn or equality fn
!(
// If the user has provided a diff function but nothing has been changed, deltaState will be null
(
deltaState === null ||
// If the user has provided an equality function, use it
options?.equality?.(pastState, currentState)
)
// If the user has provided an equality function, use it
(deltaState === null || options?.equality?.(pastState, currentState))
)
) {
curriedHandleSet(pastState, undefined, currentState, deltaState);
curriedHandleSet(pastState, currentState, deltaState);
}
};

Expand Down
20 changes: 13 additions & 7 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ type onSave<TState> =
| ((pastState: TState, currentState: TState) => void)
| undefined;

export type HandleSet<TState> = (
pastState: Partial<TState>,
currentState: Partial<TState>,
deltaState?: Partial<TState> | null,
) => void;

export interface _TemporalState<TState> {
pastStates: Partial<TState>[];
futureStates: Partial<TState>[];
Expand All @@ -16,8 +22,8 @@ export interface _TemporalState<TState> {
pause: () => void;
resume: () => void;

setOnSave: (onSave: onSave<TState>) => void;
_onSave: onSave<TState>;
setOnSave: (onSave: onSave<Partial<TState>>) => void;
_onSave: onSave<Partial<TState>>;
}

export interface ZundoOptions<TState, PartialTState = TState> {
Expand All @@ -28,11 +34,11 @@ export interface ZundoOptions<TState, PartialTState = TState> {
pastState: Partial<PartialTState>,
currentState: Partial<PartialTState>,
) => Partial<PartialTState> | null;
onSave?: onSave<TState>;
handleSet?: (handleSet: StoreApi<TState>['setState']) => (
pastState: Parameters<StoreApi<TState>['setState']>[0],
// `replace` will likely be deprecated and removed in the future
replace: Parameters<StoreApi<TState>['setState']>[1],
onSave?: onSave<Partial<TState>>;
handleSet?: (
handleSet: HandleSet<Partial<TState>>,
) => (
pastState: TState,
currentState: PartialTState,
deltaState?: Partial<PartialTState> | null,
) => void;
Expand Down
20 changes: 10 additions & 10 deletions tests/__tests__/options.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -587,9 +587,9 @@ describe('Middleware options', () => {
global.console.info = vi.fn();
const storeWithHandleSet = createVanillaStore({
handleSet: (handleSet) => {
return (state) => {
return (...args) => {
console.info('handleSet called');
handleSet(state);
handleSet(...args);
};
},
});
Expand Down Expand Up @@ -689,9 +689,9 @@ describe('Middleware options', () => {
vi.useFakeTimers();
const storeWithHandleSet = createVanillaStore({
handleSet: (handleSet) => {
return throttle<typeof handleSet>((state) => {
return throttle<typeof handleSet>((...args) => {
console.error('handleSet called');
handleSet(state);
handleSet(...args);
}, 1000);
},
});
Expand Down Expand Up @@ -781,10 +781,10 @@ describe('Middleware options', () => {
const storeWithHandleSetAndPartializeAndEquality = createVanillaStore({
handleSet: (handleSet) => {
return throttle<typeof handleSet>(
(state) => {
(...args) => {
// used for determining how many times `handleSet` is called
console.error('handleSet called');
handleSet(state);
handleSet(...args);
},
throttleIntervalInMs,
// Call throttle only on leading edge of timeout
Expand Down Expand Up @@ -833,10 +833,10 @@ describe('Middleware options', () => {
const storeWithHandleSetAndPartializeAndDiff = createVanillaStore({
handleSet: (handleSet) => {
return throttle<typeof handleSet>(
(state) => {
(...args) => {
// used for determining how many times `handleSet` is called
console.error('handleSet called');
handleSet(state);
handleSet(...args);
},
throttleIntervalInMs,
// Call throttle only on leading edge of timeout
Expand Down Expand Up @@ -900,10 +900,10 @@ describe('Middleware options', () => {
const storeWithHandleSetAndPartializeAndDiff = createVanillaStore({
handleSet: (handleSet) => {
return throttle<typeof handleSet>(
(state) => {
(...args) => {
// used for determining how many times `handleSet` is called
console.error('handleSet called');
handleSet(state);
handleSet(...args);
},
throttleIntervalInMs,
// Call throttle only on leading edge of timeout
Expand Down