Skip to content

Commit 0d9e83d

Browse files
authored
switch config for better types and save bytes (#83)
1 parent 08b7153 commit 0d9e83d

File tree

4 files changed

+66
-49
lines changed

4 files changed

+66
-49
lines changed

packages/zundo/__tests__/options.test.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -431,9 +431,9 @@ describe('Middleware options', () => {
431431

432432
describe('secret internals', () => {
433433
it('should have a secret internal state', () => {
434-
const { __handleUserSet, __onSave } =
434+
const { __handleSet, __onSave } =
435435
store.temporal.getState() as TemporalStateWithInternals<MyState>;
436-
expect(__handleUserSet).toBeInstanceOf(Function);
436+
expect(__handleSet).toBeInstanceOf(Function);
437437
expect(__onSave).toBe(undefined);
438438
});
439439
describe('onSave', () => {
@@ -508,29 +508,29 @@ describe('Middleware options', () => {
508508

509509
describe('handleUserSet', () => {
510510
it('should update the temporal store with the pastState when called', () => {
511-
const { __handleUserSet } =
511+
const { __handleSet } =
512512
store.temporal.getState() as TemporalStateWithInternals<MyState>;
513513
act(() => {
514-
__handleUserSet(store.getState());
514+
__handleSet(store.getState());
515515
});
516516
expect(store.temporal.getState().pastStates.length).toBe(1);
517517
});
518518

519519
it('should only update if the the status is tracking', () => {
520-
const { __handleUserSet } =
520+
const { __handleSet } =
521521
store.temporal.getState() as TemporalStateWithInternals<MyState>;
522522
act(() => {
523-
__handleUserSet(store.getState());
523+
__handleSet(store.getState());
524524
});
525525
expect(store.temporal.getState().pastStates.length).toBe(1);
526526
act(() => {
527527
store.temporal.getState().pause();
528-
__handleUserSet(store.getState());
528+
__handleSet(store.getState());
529529
});
530530
expect(store.temporal.getState().pastStates.length).toBe(1);
531531
act(() => {
532532
store.temporal.getState().resume();
533-
__handleUserSet(store.getState());
533+
__handleSet(store.getState());
534534
});
535535
});
536536

packages/zundo/src/index.ts

+53-38
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ import type {
55
StoreApi,
66
} from 'zustand';
77
import { createVanillaTemporal } from './temporal';
8-
import type { TemporalState, Write, ZundoOptions } from './types';
8+
import type {
9+
TemporalState,
10+
TemporalStateWithInternals,
11+
Write,
12+
ZundoOptions,
13+
} from './types';
914

1015
type Zundo = <
1116
TState,
@@ -27,51 +32,61 @@ declare module 'zustand/vanilla' {
2732
}
2833
}
2934

30-
const zundoImpl =
31-
<TState>(
32-
config: StateCreator<TState, [], []>,
33-
{
34-
partialize = (state: TState) => state,
35-
handleSet: userlandSetFactory = (handleSetCb) => handleSetCb,
36-
...restOptions
37-
} = {} as ZundoOptions<TState>,
38-
): StateCreator<TState, [], []> =>
39-
(set, get, _store) => {
40-
type TState = ReturnType<typeof config>;
41-
type StoreAddition = StoreApi<TemporalState<TState>>;
42-
43-
const temporalStore = createVanillaTemporal<TState>(set, get, partialize, restOptions);
44-
45-
const store = _store as Mutate<
46-
StoreApi<TState>,
47-
[['temporal', StoreAddition]]
48-
>;
49-
const setState = store.setState;
50-
51-
// TODO: should temporal be only temporalStore.getState()?
52-
// We can hide the rest of the store in the secret internals.
53-
store.temporal = temporalStore;
35+
const zundoImpl = <TState>(
36+
config: StateCreator<TState, [], []>,
37+
{
38+
partialize = (state: TState) => state,
39+
handleSet = (handleSetCb) => handleSetCb,
40+
...restOptions
41+
} = {} as ZundoOptions<TState>,
42+
): StateCreator<TState, [], []> => {
43+
type StoreAddition = StoreApi<TemporalState<TState>>;
44+
type StoreWithAddition = Mutate<
45+
StoreApi<TState>,
46+
[['temporal', StoreAddition]]
47+
>;
48+
const configWithTemporal = (
49+
set: StoreApi<TState>['setState'],
50+
get: StoreApi<TState>['getState'],
51+
store: StoreWithAddition,
52+
) => {
53+
store.temporal = createVanillaTemporal<TState>(
54+
set,
55+
get,
56+
partialize,
57+
restOptions,
58+
);
5459

55-
const curriedUserLandSet = userlandSetFactory(
56-
temporalStore.getState().__handleUserSet,
60+
const curriedHandleSet = handleSet(
61+
(store.temporal.getState() as TemporalStateWithInternals<TState>)
62+
.__handleSet,
5763
);
5864

59-
const modifiedSetState: typeof setState = (state, replace) => {
65+
const setState = store.setState;
66+
// Modify the setState function to call the userlandSet function
67+
store.setState = (state, replace) => {
68+
// Get most up to date state. The state from the callback might be a partial state.
69+
// The order of the get() and set() calls is important here.
6070
const pastState = partialize(get());
6171
setState(state, replace);
62-
curriedUserLandSet(pastState);
72+
curriedHandleSet(pastState);
6373
};
64-
store.setState = modifiedSetState;
6574

66-
const modifiedSetter: typeof set = (state, replace) => {
67-
// Get most up to date state. Should this be the same as the state in the callback?
68-
const pastState = partialize(get());
69-
set(state, replace);
70-
curriedUserLandSet(pastState);
71-
};
72-
73-
return config(modifiedSetter, get, _store);
75+
return config(
76+
// Modify the set function to call the userlandSet function
77+
(state, replace) => {
78+
// Get most up to date state. The state from the callback might be a partial state.
79+
// The order of the get() and set() calls is important here.
80+
const pastState = partialize(get());
81+
set(state, replace);
82+
curriedHandleSet(pastState);
83+
},
84+
get,
85+
store,
86+
);
7487
};
88+
return configWithTemporal as StateCreator<TState, [], []>;
89+
};
7590

7691
export const temporal = zundoImpl as unknown as Zundo;
7792
export type { ZundoOptions, Zundo, TemporalState };

packages/zundo/src/temporal.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export const createVanillaTemporal = <TState>(
1111
partialize: (state: TState) => TState,
1212
{ equality, onSave, limit } = {} as Omit<ZundoOptions<TState>, 'handleSet'>,
1313
) => {
14-
return createStore<TemporalStateWithInternals<TState>>()((set, get) => {
14+
15+
return createStore<TemporalStateWithInternals<TState>>((set, get) => {
1516
return {
1617
pastStates: [],
1718
futureStates: [],
@@ -68,7 +69,7 @@ export const createVanillaTemporal = <TState>(
6869
},
6970
// Internal properties
7071
__onSave: onSave,
71-
__handleUserSet: (pastState) => {
72+
__handleSet: (pastState) => {
7273
const trackingStatus = get().trackingStatus,
7374
onSave = get().__onSave,
7475
pastStates = get().pastStates.slice(),
@@ -77,6 +78,7 @@ export const createVanillaTemporal = <TState>(
7778
trackingStatus === 'tracking' &&
7879
!equality?.(currentState, pastState)
7980
) {
81+
// This naively assumes that only one new state can be added at a time
8082
if (limit && pastStates.length >= limit) {
8183
pastStates.shift();
8284
}

packages/zundo/src/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export interface TemporalStateWithInternals<TState> {
1616

1717
setOnSave: (onSave: onSave<TState>) => void;
1818
__onSave: onSave<TState>;
19-
__handleUserSet: (pastState: TState) => void;
19+
__handleSet: (pastState: TState) => void;
2020
}
2121

2222
export interface ZundoOptions<TState, PartialTState = TState> {

0 commit comments

Comments
 (0)