dev-react-microstore 1.1.3 → 3.0.0
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/dist/index.d.mts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/index.ts +118 -57
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
type StoreListener = () => void;
|
|
2
|
-
|
|
2
|
+
type StoreOptions<T extends object> = {
|
|
3
|
+
asyncUpdate?: boolean;
|
|
4
|
+
debounceDelay?: number;
|
|
5
|
+
perKeyDebounce?: Partial<Record<keyof T, boolean>>;
|
|
6
|
+
};
|
|
7
|
+
declare function createStoreState<T extends object>(initialState: T, options?: StoreOptions<T>): {
|
|
3
8
|
get: () => T;
|
|
4
9
|
set: (next: Partial<T>) => void;
|
|
5
10
|
subscribe: (keys: (keyof T)[], listener: StoreListener) => (() => void);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
type StoreListener = () => void;
|
|
2
|
-
|
|
2
|
+
type StoreOptions<T extends object> = {
|
|
3
|
+
asyncUpdate?: boolean;
|
|
4
|
+
debounceDelay?: number;
|
|
5
|
+
perKeyDebounce?: Partial<Record<keyof T, boolean>>;
|
|
6
|
+
};
|
|
7
|
+
declare function createStoreState<T extends object>(initialState: T, options?: StoreOptions<T>): {
|
|
3
8
|
get: () => T;
|
|
4
9
|
set: (next: Partial<T>) => void;
|
|
5
10
|
subscribe: (keys: (keyof T)[], listener: StoreListener) => (() => void);
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var g=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var R=(a,r)=>{for(var c in r)g(a,c,{get:r[c],enumerable:!0})},I=(a,r,c,l)=>{if(r&&typeof r=="object"||typeof r=="function")for(let y of v(r))!j.call(a,y)&&y!==c&&g(a,y,{get:()=>r[y],enumerable:!(l=P(r,y))||l.enumerable});return a};var C=a=>I(g({},"__esModule",{value:!0}),a);var D={};R(D,{createStoreState:()=>F,useStoreSelector:()=>z});module.exports=C(D);var f=require("react");function V(a,r){let c;return function(...l){c!==void 0&&clearTimeout(c),c=setTimeout(()=>a.apply(this,l),r)}}function F(a,r={}){let c=a,{asyncUpdate:l=!0,debounceDelay:y=0,perKeyDebounce:k={}}=r,T=new Map,p=new Map;return{get:()=>c,set:d=>{var o,i;if(!d)return;let t=[];for(let n in d){let e=n,u=c[e],s=d[e];u!==s&&(Object.is(u,s)||(c[e]=s,t.push(e)))}if(t.length!==0)if(l)for(let n of t)k[n]!==!1?(p.has(n)||p.set(n,V(()=>{var u;(u=T.get(n))==null||u.forEach(s=>s())},y)),p.get(n)()):(o=T.get(n))==null||o.forEach(u=>u());else for(let n of t)(i=T.get(n))==null||i.forEach(e=>e())},subscribe:(d,t)=>{for(let o of d)T.has(o)||T.set(o,new Set),T.get(o).add(t);return()=>{var o;for(let i of d)(o=T.get(i))==null||o.delete(t)}},select:d=>{let t={},o=c;for(let i of d)t[i]=o[i];return t}}}function E(a,r){return a.length===r.length&&a.every((c,l)=>c===r[l])}function z(a,r){let c=(0,f.useRef)({}),l=(0,f.useRef)(null),y=(0,f.useRef)(null),k=(0,f.useRef)(null),T=(0,f.useRef)(!0),p=(0,f.useRef)({}),b=(0,f.useRef)(null);if(!l.current||!E(l.current,r)){let t=[],o=[];for(let i of r)if(typeof i=="string"){let n=i;t.push({key:n}),o.push(n)}else{let n=i;for(let e in n){let u=n[e],s=e;t.push({key:s,compare:u}),o.push(s)}}y.current=t,k.current=o,l.current=r}let h=y.current,m=k.current,K=()=>{let t=a.get();if(T.current){T.current=!1;let e={};for(let{key:u}of h){let s=t[u];p.current[u]=s,e[u]=s}return c.current=e,e}if(!(()=>{var e;for(let{key:u,compare:s}of h){let S=p.current[u],x=t[u];if(S===void 0||((e=s==null?void 0:s(S,x))!=null?e:!Object.is(S,x)))return!0}return!1})())return c.current;let n={};for(let{key:e,compare:u}of h){let s=p.current[e],S=t[e];s===void 0||(u?!u(s,S):!Object.is(s,S))?(p.current[e]=S,n[e]=S):n[e]=s}return c.current=n,n},d=(0,f.useMemo)(()=>{let t=a.get(),o={};for(let i of m)o[i]=t[i];return o},[m]);if(!b.current||k.current!==m){let t=m;b.current=o=>a.subscribe(t,o)}return(0,f.useSyncExternalStore)(b.current,K,()=>d)}0&&(module.exports={createStoreState,useStoreSelector});
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useRef as
|
|
1
|
+
import{useMemo as K,useRef as p,useSyncExternalStore as P}from"react";function v(a,i){let u;return function(...y){u!==void 0&&clearTimeout(u),u=setTimeout(()=>a.apply(this,y),i)}}function C(a,i={}){let u=a,{asyncUpdate:y=!0,debounceDelay:m=0,perKeyDebounce:S={}}=i,l=new Map,T=new Map;return{get:()=>u,set:f=>{var o,c;if(!f)return;let t=[];for(let n in f){let e=n,s=u[e],r=f[e];s!==r&&(Object.is(s,r)||(u[e]=r,t.push(e)))}if(t.length!==0)if(y)for(let n of t)S[n]!==!1?(T.has(n)||T.set(n,v(()=>{var s;(s=l.get(n))==null||s.forEach(r=>r())},m)),T.get(n)()):(o=l.get(n))==null||o.forEach(s=>s());else for(let n of t)(c=l.get(n))==null||c.forEach(e=>e())},subscribe:(f,t)=>{for(let o of f)l.has(o)||l.set(o,new Set),l.get(o).add(t);return()=>{var o;for(let c of f)(o=l.get(c))==null||o.delete(t)}},select:f=>{let t={},o=u;for(let c of f)t[c]=o[c];return t}}}function j(a,i){return a.length===i.length&&a.every((u,y)=>u===i[y])}function V(a,i){let u=p({}),y=p(null),m=p(null),S=p(null),l=p(!0),T=p({}),b=p(null);if(!y.current||!j(y.current,i)){let t=[],o=[];for(let c of i)if(typeof c=="string"){let n=c;t.push({key:n}),o.push(n)}else{let n=c;for(let e in n){let s=n[e],r=e;t.push({key:r,compare:s}),o.push(r)}}m.current=t,S.current=o,y.current=i}let h=m.current,k=S.current,g=()=>{let t=a.get();if(l.current){l.current=!1;let e={};for(let{key:s}of h){let r=t[s];T.current[s]=r,e[s]=r}return u.current=e,e}if(!(()=>{var e;for(let{key:s,compare:r}of h){let d=T.current[s],x=t[s];if(d===void 0||((e=r==null?void 0:r(d,x))!=null?e:!Object.is(d,x)))return!0}return!1})())return u.current;let n={};for(let{key:e,compare:s}of h){let r=T.current[e],d=t[e];r===void 0||(s?!s(r,d):!Object.is(r,d))?(T.current[e]=d,n[e]=d):n[e]=r}return u.current=n,n},f=K(()=>{let t=a.get(),o={};for(let c of k)o[c]=t[c];return o},[k]);if(!b.current||S.current!==k){let t=k;b.current=o=>a.subscribe(t,o)}return P(b.current,g,()=>f)}export{C as createStoreState,V as useStoreSelector};
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,36 +1,79 @@
|
|
|
1
|
-
import { useRef, useSyncExternalStore } from 'react'
|
|
1
|
+
import { useMemo, useRef, useSyncExternalStore } from 'react'
|
|
2
|
+
|
|
2
3
|
type StoreListener = () => void;
|
|
3
4
|
|
|
5
|
+
function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): T {
|
|
6
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
7
|
+
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
|
|
8
|
+
if (timeoutId !== undefined) {
|
|
9
|
+
clearTimeout(timeoutId);
|
|
10
|
+
}
|
|
11
|
+
timeoutId = setTimeout(() => fn.apply(this, args), delay);
|
|
12
|
+
} as T;
|
|
13
|
+
}
|
|
4
14
|
|
|
15
|
+
type StoreOptions<T extends object> = {
|
|
16
|
+
asyncUpdate?: boolean;
|
|
17
|
+
debounceDelay?: number;
|
|
18
|
+
perKeyDebounce?: Partial<Record<keyof T, boolean>>;
|
|
19
|
+
};
|
|
5
20
|
|
|
6
|
-
export function createStoreState<T extends object>(
|
|
21
|
+
export function createStoreState<T extends object>(
|
|
22
|
+
initialState: T,
|
|
23
|
+
options: StoreOptions<T> = {}
|
|
24
|
+
) {
|
|
7
25
|
let state = initialState;
|
|
8
|
-
|
|
26
|
+
const {
|
|
27
|
+
asyncUpdate = true,
|
|
28
|
+
debounceDelay = 0,
|
|
29
|
+
perKeyDebounce = {} as Partial<Record<keyof T, boolean>>
|
|
30
|
+
} = options;
|
|
9
31
|
const keyListeners = new Map<keyof T, Set<StoreListener>>();
|
|
32
|
+
const debouncedNotifiers = new Map<keyof T, () => void>();
|
|
10
33
|
|
|
11
34
|
const get = () => state;
|
|
12
35
|
|
|
13
36
|
const set = (next: Partial<T>) => {
|
|
14
|
-
|
|
37
|
+
if (!next) return;
|
|
15
38
|
|
|
39
|
+
const updatedKeys: (keyof T)[] = [];
|
|
16
40
|
for (const key in next) {
|
|
17
41
|
const typedKey = key as keyof T;
|
|
18
|
-
|
|
42
|
+
const currentValue = state[typedKey];
|
|
43
|
+
const nextValue = next[typedKey];
|
|
44
|
+
|
|
45
|
+
if (currentValue === nextValue) continue;
|
|
46
|
+
|
|
47
|
+
if (!Object.is(currentValue, nextValue)) {
|
|
48
|
+
state[typedKey] = nextValue!;
|
|
19
49
|
updatedKeys.push(typedKey);
|
|
20
50
|
}
|
|
21
51
|
}
|
|
22
52
|
|
|
23
53
|
if (updatedKeys.length === 0) return;
|
|
24
54
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
55
|
+
if (asyncUpdate) {
|
|
56
|
+
for (const key of updatedKeys) {
|
|
57
|
+
const shouldDebounce = perKeyDebounce[key] !== false;
|
|
58
|
+
|
|
59
|
+
if (shouldDebounce) {
|
|
60
|
+
if (!debouncedNotifiers.has(key)) {
|
|
61
|
+
debouncedNotifiers.set(key, debounce(() => {
|
|
62
|
+
keyListeners.get(key)?.forEach(listener => listener());
|
|
63
|
+
}, debounceDelay));
|
|
64
|
+
}
|
|
65
|
+
debouncedNotifiers.get(key)!();
|
|
66
|
+
} else {
|
|
67
|
+
keyListeners.get(key)?.forEach(listener => listener());
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
for (const key of updatedKeys) {
|
|
72
|
+
keyListeners.get(key)?.forEach(listener => listener());
|
|
73
|
+
}
|
|
31
74
|
}
|
|
32
75
|
};
|
|
33
|
-
|
|
76
|
+
|
|
34
77
|
const subscribe = (keys: (keyof T)[], listener: StoreListener): (() => void) => {
|
|
35
78
|
for (const key of keys) {
|
|
36
79
|
if (!keyListeners.has(key)) {
|
|
@@ -68,10 +111,10 @@ type SelectorInput<T extends object> = ReadonlyArray<KeySelector<T> | CustomSele
|
|
|
68
111
|
|
|
69
112
|
type ExtractSelectorKeys<T extends object, S extends SelectorInput<T>> = {
|
|
70
113
|
[K in S[number] extends infer Item
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
114
|
+
? Item extends keyof T
|
|
115
|
+
? Item
|
|
116
|
+
: keyof Item
|
|
117
|
+
: never]: T[K];
|
|
75
118
|
};
|
|
76
119
|
|
|
77
120
|
type Picked<T extends object, S extends SelectorInput<T>> = ExtractSelectorKeys<T, S>;
|
|
@@ -88,21 +131,6 @@ function shallowEqualSelector<T extends object>(
|
|
|
88
131
|
return a.length === b.length && a.every((item, i) => item === b[i]);
|
|
89
132
|
}
|
|
90
133
|
|
|
91
|
-
function pickKeys<T extends object>(
|
|
92
|
-
base: T,
|
|
93
|
-
prev: Partial<T>,
|
|
94
|
-
selectors: NormalizedSelector<T>[]
|
|
95
|
-
): Partial<T> {
|
|
96
|
-
const out: Partial<T> = {};
|
|
97
|
-
for (const { key, compare } of selectors) {
|
|
98
|
-
const prevVal = prev[key];
|
|
99
|
-
const nextVal = base[key];
|
|
100
|
-
const changed = prevVal === undefined ? true : (compare ? compare(prevVal, nextVal) : !Object.is(prevVal, nextVal));
|
|
101
|
-
out[key] = changed ? nextVal : prevVal;
|
|
102
|
-
}
|
|
103
|
-
return out;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
134
|
export function useStoreSelector<T extends object, S extends SelectorInput<T>>(
|
|
107
135
|
store: StoreType<T>,
|
|
108
136
|
selector: S
|
|
@@ -113,25 +141,28 @@ export function useStoreSelector<T extends object, S extends SelectorInput<T>>(
|
|
|
113
141
|
const keysRef = useRef<(keyof T)[] | null>(null);
|
|
114
142
|
const isFirstRunRef = useRef(true);
|
|
115
143
|
const lastValues = useRef<Partial<T>>({});
|
|
144
|
+
const subscribeRef = useRef<((onStoreChange: () => void) => () => void) | null>(null);
|
|
116
145
|
|
|
117
146
|
if (!prevSelector.current || !shallowEqualSelector(prevSelector.current, selector)) {
|
|
118
147
|
const normalized: NormalizedSelector<T>[] = [];
|
|
119
148
|
const keys: (keyof T)[] = [];
|
|
120
|
-
|
|
149
|
+
|
|
121
150
|
for (const item of selector) {
|
|
122
151
|
if (typeof item === 'string') {
|
|
123
152
|
const key = item as keyof T;
|
|
124
153
|
normalized.push({ key });
|
|
125
154
|
keys.push(key);
|
|
126
155
|
} else {
|
|
127
|
-
|
|
156
|
+
const customSelector = item as CustomSelector<T>;
|
|
157
|
+
for (const key in customSelector) {
|
|
158
|
+
const compare = customSelector[key as keyof typeof customSelector];
|
|
128
159
|
const typedKey = key as keyof T;
|
|
129
160
|
normalized.push({ key: typedKey, compare: compare as CompareFn<T[keyof T]> });
|
|
130
161
|
keys.push(typedKey);
|
|
131
162
|
}
|
|
132
163
|
}
|
|
133
164
|
}
|
|
134
|
-
|
|
165
|
+
|
|
135
166
|
normalizedRef.current = normalized;
|
|
136
167
|
keysRef.current = keys;
|
|
137
168
|
prevSelector.current = selector;
|
|
@@ -144,38 +175,68 @@ export function useStoreSelector<T extends object, S extends SelectorInput<T>>(
|
|
|
144
175
|
const current = store.get();
|
|
145
176
|
const isFirstRun = isFirstRunRef.current;
|
|
146
177
|
|
|
147
|
-
const changed = isFirstRun || normalized.some(({ key, compare }) => {
|
|
148
|
-
const prevVal = lastValues.current[key];
|
|
149
|
-
const nextVal = current[key];
|
|
150
|
-
return prevVal === undefined ? true : (compare?.(prevVal, nextVal) ?? !Object.is(prevVal, nextVal));
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
if (!changed) {
|
|
154
|
-
return lastSelected.current as Picked<T, S>;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
178
|
if (isFirstRun) {
|
|
158
179
|
isFirstRunRef.current = false;
|
|
180
|
+
const result = {} as Partial<T>;
|
|
181
|
+
for (const { key } of normalized) {
|
|
182
|
+
const value = current[key];
|
|
183
|
+
lastValues.current[key] = value;
|
|
184
|
+
result[key] = value;
|
|
185
|
+
}
|
|
186
|
+
lastSelected.current = result;
|
|
187
|
+
return result as Picked<T, S>;
|
|
159
188
|
}
|
|
160
189
|
|
|
161
|
-
|
|
162
|
-
|
|
190
|
+
const hasChanges = () => {
|
|
191
|
+
for (const { key, compare } of normalized) {
|
|
192
|
+
const prevVal = lastValues.current[key];
|
|
193
|
+
const nextVal = current[key];
|
|
194
|
+
if (prevVal === undefined ? true : (compare?.(prevVal, nextVal) ?? !Object.is(prevVal, nextVal))) {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return false;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
if (!hasChanges()) {
|
|
202
|
+
return lastSelected.current as Picked<T, S>;
|
|
163
203
|
}
|
|
164
204
|
|
|
165
|
-
|
|
166
|
-
|
|
205
|
+
const result = {} as Partial<T>;
|
|
206
|
+
for (const { key, compare } of normalized) {
|
|
207
|
+
const prevVal = lastValues.current[key];
|
|
208
|
+
const nextVal = current[key];
|
|
209
|
+
|
|
210
|
+
const isFirstTime = prevVal === undefined;
|
|
211
|
+
const changed = isFirstTime || (compare ? !compare(prevVal, nextVal) : !Object.is(prevVal, nextVal));
|
|
212
|
+
|
|
213
|
+
if (changed) {
|
|
214
|
+
lastValues.current[key] = nextVal;
|
|
215
|
+
result[key] = nextVal;
|
|
216
|
+
} else {
|
|
217
|
+
result[key] = prevVal;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
lastSelected.current = result;
|
|
222
|
+
return result as Picked<T, S>;
|
|
167
223
|
};
|
|
168
224
|
|
|
169
|
-
const staticSnapshot = (() => {
|
|
225
|
+
const staticSnapshot = useMemo(() => {
|
|
170
226
|
const current = store.get();
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
|
|
227
|
+
const result = {} as Partial<T>;
|
|
228
|
+
for (const key of keys) {
|
|
229
|
+
result[key] = current[key];
|
|
230
|
+
}
|
|
231
|
+
return result as Picked<T, S>;
|
|
232
|
+
}, [keys]);
|
|
176
233
|
|
|
177
|
-
const subscribe = (onStoreChange: () => void) =>
|
|
178
|
-
store.subscribe(keys, onStoreChange);
|
|
179
234
|
|
|
180
|
-
|
|
235
|
+
if (!subscribeRef.current || keysRef.current !== keys) {
|
|
236
|
+
const currentKeys = keys;
|
|
237
|
+
subscribeRef.current = (onStoreChange: () => void) =>
|
|
238
|
+
store.subscribe(currentKeys, onStoreChange);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return useSyncExternalStore(subscribeRef.current, getSnapshot, () => staticSnapshot);
|
|
181
242
|
}
|