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