pocket-state 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/globalState/event.ts +9 -0
- package/src/globalState/hooks.ts +19 -47
- package/src/globalState/store.ts +18 -19
- package/src/globalState/type.d.ts +6 -0
package/package.json
CHANGED
package/src/globalState/event.ts
CHANGED
|
@@ -78,4 +78,13 @@ export class EventEmitter implements IEventEmitter {
|
|
|
78
78
|
this.events.clear();
|
|
79
79
|
this.onceWrappers.clear();
|
|
80
80
|
}
|
|
81
|
+
|
|
82
|
+
getNumberOfSubscriber(event?: string): number {
|
|
83
|
+
if (event) {
|
|
84
|
+
return this.events.get(event)?.size ?? 0;
|
|
85
|
+
}
|
|
86
|
+
let total = 0;
|
|
87
|
+
this.events.forEach(set => (total += set.size));
|
|
88
|
+
return total;
|
|
89
|
+
}
|
|
81
90
|
}
|
package/src/globalState/hooks.ts
CHANGED
|
@@ -1,58 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
import {useCallback, useRef} from 'react';
|
|
3
|
-
import {useSyncExternalStore} from 'react';
|
|
1
|
+
import {useCallback, useRef, useSyncExternalStore} from 'react';
|
|
4
2
|
import type {Store} from './type';
|
|
5
|
-
import {shallow} from './shallowEqual';
|
|
6
|
-
|
|
7
|
-
export function useStore<T>(store: Store<T>): T;
|
|
8
|
-
export function useStore<T, S>(store: Store<T>, selector: (state: T) => S): S;
|
|
9
3
|
|
|
10
4
|
export function useStore<T, S = T>(
|
|
11
5
|
store: Store<T>,
|
|
12
6
|
selector?: (state: T) => S,
|
|
13
|
-
): T | S {
|
|
14
|
-
const subscribe = useCallback(
|
|
15
|
-
(onChange: () => void) =>
|
|
16
|
-
selector
|
|
17
|
-
? store.subscribe(selector, () => onChange())
|
|
18
|
-
: store.subscribe(() => onChange()),
|
|
19
|
-
[store, selector],
|
|
20
|
-
);
|
|
21
|
-
const getSnapshot = useCallback(() => {
|
|
22
|
-
const s = store.getValue();
|
|
23
|
-
return selector ? selector(s) : (s as T);
|
|
24
|
-
}, [store, selector]);
|
|
25
|
-
|
|
26
|
-
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot) as T | S;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
//shallow
|
|
30
|
-
export function useShallowStore<T, S>(
|
|
31
|
-
store: Store<T>,
|
|
32
|
-
selector: (state: T) => S,
|
|
33
7
|
): S {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const lastSliceRef = useRef<S>(selector(store.getValue()));
|
|
8
|
+
const sliceRef = useRef<S>(
|
|
9
|
+
selector ? selector(store.getValue()) : (store.getValue() as unknown as S),
|
|
10
|
+
);
|
|
38
11
|
|
|
39
|
-
const subscribe = (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
12
|
+
const subscribe = useCallback(
|
|
13
|
+
(onChange: () => void) => {
|
|
14
|
+
if (selector) {
|
|
15
|
+
return store.subscribe(selector, (nextSlice: S) => {
|
|
16
|
+
sliceRef.current = nextSlice;
|
|
17
|
+
onChange();
|
|
18
|
+
});
|
|
45
19
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return lastSliceRef.current;
|
|
55
|
-
};
|
|
20
|
+
return store.subscribe((next: T) => {
|
|
21
|
+
sliceRef.current = next as unknown as S;
|
|
22
|
+
onChange();
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
[store, selector],
|
|
26
|
+
);
|
|
27
|
+
const getSnapshot = useCallback(() => sliceRef.current, []);
|
|
56
28
|
|
|
57
29
|
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
58
30
|
}
|
package/src/globalState/store.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
// store.ts
|
|
2
|
-
// import deepEqual from 'fast-deep-equal';
|
|
3
2
|
import {IEventEmitter, Listener, Middleware, Store, UseStoreGet} from './type';
|
|
4
3
|
import {EventEmitter} from './event';
|
|
5
4
|
import {Draft, produce} from 'immer';
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import {shallow} from './shallowEqual';
|
|
6
|
+
|
|
8
7
|
export function createStore<T>(
|
|
9
8
|
initialState: T,
|
|
10
9
|
middlewares: Middleware<T>[] = [],
|
|
10
|
+
equalityFn?: (a: any, b: any) => boolean,
|
|
11
11
|
): Store<T> {
|
|
12
12
|
const emitter: IEventEmitter = new EventEmitter();
|
|
13
13
|
let state = initialState;
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
const areEqual = equalityFn ?? shallow;
|
|
16
|
+
|
|
16
17
|
let emitScheduled = false;
|
|
17
18
|
const emitState = () => {
|
|
18
19
|
if (emitScheduled) return;
|
|
@@ -25,13 +26,12 @@ export function createStore<T>(
|
|
|
25
26
|
|
|
26
27
|
function baseSet(delta: Partial<T>) {
|
|
27
28
|
const nextState = Array.isArray(state)
|
|
28
|
-
? (delta as unknown as T)
|
|
29
|
-
: {...state, ...delta};
|
|
29
|
+
? (delta as unknown as T)
|
|
30
|
+
: {...state, ...delta};
|
|
30
31
|
|
|
31
|
-
if (!
|
|
32
|
+
if (!areEqual(state, nextState)) {
|
|
32
33
|
state = nextState;
|
|
33
34
|
|
|
34
|
-
// Dev guard: phát hiện mutate ngoài store
|
|
35
35
|
if (process.env.NODE_ENV !== 'production') {
|
|
36
36
|
try {
|
|
37
37
|
Object.freeze(state as any);
|
|
@@ -61,22 +61,20 @@ export function createStore<T>(
|
|
|
61
61
|
let wrapped: Listener<any>;
|
|
62
62
|
|
|
63
63
|
if (typeof maybeListener === 'function') {
|
|
64
|
-
// subscribe(selector, listener)
|
|
65
64
|
const selector: (s: T) => any = selectorOrListener;
|
|
66
65
|
let prevSlice = selector(state);
|
|
67
66
|
wrapped = (next: T) => {
|
|
68
67
|
const slice = selector(next);
|
|
69
|
-
if (!
|
|
68
|
+
if (!areEqual(slice, prevSlice)) {
|
|
70
69
|
prevSlice = slice;
|
|
71
70
|
maybeListener(slice);
|
|
72
71
|
}
|
|
73
72
|
};
|
|
74
73
|
} else {
|
|
75
|
-
// subscribe(listener)
|
|
76
74
|
const listener: Listener<T> = selectorOrListener;
|
|
77
75
|
let prev = state;
|
|
78
76
|
wrapped = (next: T) => {
|
|
79
|
-
if (!
|
|
77
|
+
if (!areEqual(next, prev)) {
|
|
80
78
|
prev = next;
|
|
81
79
|
listener(next);
|
|
82
80
|
}
|
|
@@ -84,10 +82,6 @@ export function createStore<T>(
|
|
|
84
82
|
}
|
|
85
83
|
|
|
86
84
|
emitter.on('state', wrapped);
|
|
87
|
-
|
|
88
|
-
// Lưu ý: KHÔNG gọi listener ngay khi subscribe
|
|
89
|
-
// (để tương thích useSyncExternalStore: initial snapshot lấy qua getSnapshot)
|
|
90
|
-
|
|
91
85
|
return () => emitter.off('state', wrapped);
|
|
92
86
|
}
|
|
93
87
|
|
|
@@ -111,7 +105,7 @@ export function createStore<T>(
|
|
|
111
105
|
function setImmer(updater: (draft: Draft<T>) => void): void {
|
|
112
106
|
try {
|
|
113
107
|
const nextState = produce(state, updater);
|
|
114
|
-
if (
|
|
108
|
+
if (areEqual(state, nextState)) return;
|
|
115
109
|
|
|
116
110
|
if (Array.isArray(state)) {
|
|
117
111
|
setFn(nextState as unknown as Partial<T>);
|
|
@@ -168,7 +162,7 @@ export function createStore<T>(
|
|
|
168
162
|
}
|
|
169
163
|
}
|
|
170
164
|
const current = getValue();
|
|
171
|
-
if (!
|
|
165
|
+
if (!areEqual(current, next)) {
|
|
172
166
|
setFn(next as unknown as Partial<T>);
|
|
173
167
|
}
|
|
174
168
|
}
|
|
@@ -184,7 +178,11 @@ export function createStore<T>(
|
|
|
184
178
|
}
|
|
185
179
|
|
|
186
180
|
function isDirty() {
|
|
187
|
-
return !
|
|
181
|
+
return !areEqual(state, initialState);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getNumberOfSubscriber() {
|
|
185
|
+
return emitter.getNumberOfSubscriber();
|
|
188
186
|
}
|
|
189
187
|
return {
|
|
190
188
|
getValue,
|
|
@@ -194,5 +192,6 @@ export function createStore<T>(
|
|
|
194
192
|
reset,
|
|
195
193
|
subscribe,
|
|
196
194
|
isDirty,
|
|
195
|
+
getNumberOfSubscriber,
|
|
197
196
|
};
|
|
198
197
|
}
|
|
@@ -97,6 +97,9 @@ export interface IEventEmitter {
|
|
|
97
97
|
|
|
98
98
|
/** Remove all listeners for all events. */
|
|
99
99
|
clear(): void;
|
|
100
|
+
|
|
101
|
+
/** Get number of subscribe */
|
|
102
|
+
getNumberOfSubscriber(event?: string): number;
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
/**
|
|
@@ -194,6 +197,9 @@ export interface Store<T> {
|
|
|
194
197
|
* ```
|
|
195
198
|
*/
|
|
196
199
|
isDirty(): boolean;
|
|
200
|
+
|
|
201
|
+
/** Get number of subscriber for current store */
|
|
202
|
+
getNumberOfSubscriber(): number;
|
|
197
203
|
}
|
|
198
204
|
|
|
199
205
|
/**
|