pocket-state 0.0.2 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pocket-state",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "tiny global store",
5
5
  "main": "src/index",
6
6
  "codegenConfig": {
@@ -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
  }
@@ -1,26 +1,30 @@
1
- // useStore.ts
2
- import {useCallback} from 'react';
3
- import {useSyncExternalStore} from 'react';
1
+ import {useCallback, useRef, useSyncExternalStore} from 'react';
4
2
  import type {Store} from './type';
5
3
 
6
- export function useStore<T>(store: Store<T>): T;
7
- export function useStore<T, S>(store: Store<T>, selector: (state: T) => S): S;
8
-
9
4
  export function useStore<T, S = T>(
10
5
  store: Store<T>,
11
6
  selector?: (state: T) => S,
12
- ): T | S {
7
+ ): S {
8
+ const sliceRef = useRef<S>(
9
+ selector ? selector(store.getValue()) : (store.getValue() as unknown as S),
10
+ );
11
+
13
12
  const subscribe = useCallback(
14
- (onChange: () => void) =>
15
- selector
16
- ? store.subscribe(selector, () => onChange())
17
- : store.subscribe(() => onChange()),
13
+ (onChange: () => void) => {
14
+ if (selector) {
15
+ return store.subscribe(selector, (nextSlice: S) => {
16
+ sliceRef.current = nextSlice;
17
+ onChange();
18
+ });
19
+ }
20
+ return store.subscribe((next: T) => {
21
+ sliceRef.current = next as unknown as S;
22
+ onChange();
23
+ });
24
+ },
18
25
  [store, selector],
19
26
  );
20
- const getSnapshot = useCallback(() => {
21
- const s = store.getValue();
22
- return selector ? selector(s) : (s as T);
23
- }, [store, selector]);
27
+ const getSnapshot = useCallback(() => sliceRef.current, []);
24
28
 
25
- return useSyncExternalStore(subscribe, getSnapshot, getSnapshot) as T | S;
29
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
26
30
  }
@@ -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
- // import {shallow} from './shallowEqual';
7
- import deepEqual from 'fast-deep-equal';
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
- // Coalesce emits trong cùng 1 microtask
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) // với array: replace
29
- : {...state, ...delta}; // với object: shallow merge
29
+ ? (delta as unknown as T)
30
+ : {...state, ...delta};
30
31
 
31
- if (!deepEqual(state, nextState)) {
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 (!deepEqual(slice, prevSlice)) {
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 (!deepEqual(next, prev)) {
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 (deepEqual(state, nextState)) return;
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 (!deepEqual(current, next)) {
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 !deepEqual(state, initialState);
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
  /**