@montra-interactive/deepstate-react 0.3.4 → 0.3.6-alpha.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/hooks.d.ts.map +1 -1
- package/dist/index.js +49 -18
- package/package.json +2 -2
- package/src/hooks.ts +100 -20
package/dist/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAwBvC;;;GAGG;AACH,UAAU,WAAW,CAAC,CAAC;IACrB,GAAG,IAAI,CAAC,CAAC;CACV;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,34 @@ function isObservable(obj) {
|
|
|
20
20
|
return false;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
+
function useStableNodes(nodeOrNodes) {
|
|
24
|
+
const ref = useRef(nodeOrNodes);
|
|
25
|
+
if (Array.isArray(nodeOrNodes)) {
|
|
26
|
+
const prev = ref.current;
|
|
27
|
+
if (!Array.isArray(prev) || prev.length !== nodeOrNodes.length || nodeOrNodes.some((n, i) => n !== prev[i])) {
|
|
28
|
+
ref.current = nodeOrNodes;
|
|
29
|
+
}
|
|
30
|
+
return ref.current;
|
|
31
|
+
}
|
|
32
|
+
if (!isObservable(nodeOrNodes)) {
|
|
33
|
+
const prev = ref.current;
|
|
34
|
+
if (isObservable(prev) || Array.isArray(prev)) {
|
|
35
|
+
ref.current = nodeOrNodes;
|
|
36
|
+
return ref.current;
|
|
37
|
+
}
|
|
38
|
+
const prevObj = prev;
|
|
39
|
+
const currKeys = Object.keys(nodeOrNodes);
|
|
40
|
+
const prevKeys = Object.keys(prevObj);
|
|
41
|
+
if (currKeys.length !== prevKeys.length || currKeys.some((k) => nodeOrNodes[k] !== prevObj[k])) {
|
|
42
|
+
ref.current = nodeOrNodes;
|
|
43
|
+
}
|
|
44
|
+
return ref.current;
|
|
45
|
+
}
|
|
46
|
+
if (nodeOrNodes !== ref.current) {
|
|
47
|
+
ref.current = nodeOrNodes;
|
|
48
|
+
}
|
|
49
|
+
return ref.current;
|
|
50
|
+
}
|
|
23
51
|
function useObservable(observable$, getSnapshot) {
|
|
24
52
|
const valueRef = useRef(getSnapshot());
|
|
25
53
|
const subscribe = useCallback((onStoreChange) => {
|
|
@@ -33,55 +61,58 @@ function useObservable(observable$, getSnapshot) {
|
|
|
33
61
|
return useSyncExternalStore(subscribe, getSnapshotMemo, getSnapshotMemo);
|
|
34
62
|
}
|
|
35
63
|
function useSelect(nodeOrNodes, selector, equalityFn = Object.is) {
|
|
64
|
+
const selectorRef = useRef(selector);
|
|
65
|
+
selectorRef.current = selector;
|
|
66
|
+
const equalityFnRef = useRef(equalityFn);
|
|
67
|
+
equalityFnRef.current = equalityFn;
|
|
68
|
+
const stableNodes = useStableNodes(nodeOrNodes);
|
|
36
69
|
const { combined$, getInitialValue } = useMemo(() => {
|
|
37
|
-
if (Array.isArray(
|
|
38
|
-
const nodes =
|
|
39
|
-
const sel = selector;
|
|
70
|
+
if (Array.isArray(stableNodes)) {
|
|
71
|
+
const nodes = stableNodes;
|
|
40
72
|
return {
|
|
41
|
-
combined$: combineLatest(nodes).pipe(map((values) =>
|
|
73
|
+
combined$: combineLatest(nodes).pipe(distinctUntilChanged((a, b) => a.length === b.length && a.every((v, i) => Object.is(v, b[i]))), map((values) => selectorRef.current(values)), distinctUntilChanged((a, b) => equalityFnRef.current(a, b))),
|
|
42
74
|
getInitialValue: () => {
|
|
43
75
|
const values = nodes.map((n) => hasGet(n) ? n.get() : undefined);
|
|
44
|
-
return
|
|
76
|
+
return selectorRef.current(values);
|
|
45
77
|
}
|
|
46
78
|
};
|
|
47
79
|
}
|
|
48
|
-
if (!isObservable(
|
|
49
|
-
const obj =
|
|
80
|
+
if (!isObservable(stableNodes)) {
|
|
81
|
+
const obj = stableNodes;
|
|
50
82
|
const keys = Object.keys(obj);
|
|
51
83
|
const observables = keys.map((k) => obj[k]);
|
|
52
|
-
const sel = selector;
|
|
53
84
|
return {
|
|
54
|
-
combined$: combineLatest(observables).pipe(map((values) => {
|
|
85
|
+
combined$: combineLatest(observables).pipe(distinctUntilChanged((a, b) => a.length === b.length && a.every((v, i) => Object.is(v, b[i]))), map((values) => {
|
|
55
86
|
const result = {};
|
|
56
87
|
keys.forEach((key, i) => {
|
|
57
88
|
result[key] = values[i];
|
|
58
89
|
});
|
|
59
|
-
return
|
|
60
|
-
}), distinctUntilChanged(
|
|
90
|
+
return selectorRef.current(result);
|
|
91
|
+
}), distinctUntilChanged((a, b) => equalityFnRef.current(a, b))),
|
|
61
92
|
getInitialValue: () => {
|
|
62
93
|
const result = {};
|
|
63
94
|
keys.forEach((key) => {
|
|
64
95
|
const node2 = obj[key];
|
|
65
96
|
result[key] = hasGet(node2) ? node2.get() : undefined;
|
|
66
97
|
});
|
|
67
|
-
return
|
|
98
|
+
return selectorRef.current(result);
|
|
68
99
|
}
|
|
69
100
|
};
|
|
70
101
|
}
|
|
71
|
-
const node =
|
|
72
|
-
if (
|
|
102
|
+
const node = stableNodes;
|
|
103
|
+
if (selectorRef.current) {
|
|
73
104
|
return {
|
|
74
|
-
combined$: node.pipe(map((value) =>
|
|
105
|
+
combined$: node.pipe(distinctUntilChanged(), map((value) => selectorRef.current(value)), distinctUntilChanged((a, b) => equalityFnRef.current(a, b))),
|
|
75
106
|
getInitialValue: () => {
|
|
76
107
|
if (hasGet(node)) {
|
|
77
|
-
return
|
|
108
|
+
return selectorRef.current(node.get());
|
|
78
109
|
}
|
|
79
110
|
return;
|
|
80
111
|
}
|
|
81
112
|
};
|
|
82
113
|
} else {
|
|
83
114
|
return {
|
|
84
|
-
combined$: node.pipe(distinctUntilChanged(
|
|
115
|
+
combined$: node.pipe(distinctUntilChanged((a, b) => equalityFnRef.current(a, b))),
|
|
85
116
|
getInitialValue: () => {
|
|
86
117
|
if (hasGet(node)) {
|
|
87
118
|
return node.get();
|
|
@@ -90,7 +121,7 @@ function useSelect(nodeOrNodes, selector, equalityFn = Object.is) {
|
|
|
90
121
|
}
|
|
91
122
|
};
|
|
92
123
|
}
|
|
93
|
-
}, [
|
|
124
|
+
}, [stableNodes]);
|
|
94
125
|
const valueRef = useRef(getInitialValue());
|
|
95
126
|
const subscribe = useCallback((onStoreChange) => {
|
|
96
127
|
const subscription = combined$.subscribe((newValue) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@montra-interactive/deepstate-react",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6-alpha.0",
|
|
4
4
|
"description": "React bindings for deepstate - Proxy-based reactive state management with RxJS.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"react": "^18 || ^19",
|
|
43
43
|
"rxjs": "^7",
|
|
44
|
-
"@montra-interactive/deepstate": "^0.
|
|
44
|
+
"@montra-interactive/deepstate": "^0.4.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@testing-library/react": "^16.3.1",
|
package/src/hooks.ts
CHANGED
|
@@ -59,6 +59,62 @@ function isObservable(obj: unknown): obj is Observable<unknown> {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Stabilizes the identity of nodeOrNodes across re-renders.
|
|
64
|
+
*
|
|
65
|
+
* Users typically pass inline arrays like `[store.a, store.b]` or inline objects
|
|
66
|
+
* like `{ a: store.a }` to useSelect. These are new references every render,
|
|
67
|
+
* which would cause useMemo to recreate the observable pipeline, leading to
|
|
68
|
+
* resubscription and potential infinite render loops (shareReplay replays the
|
|
69
|
+
* last value → onStoreChange → re-render → new useMemo → resubscribe → replay → …).
|
|
70
|
+
*
|
|
71
|
+
* This hook compares the individual node references inside the container and
|
|
72
|
+
* returns a stable reference as long as the nodes themselves haven't changed.
|
|
73
|
+
*/
|
|
74
|
+
type NodeInput = Observable<unknown> | Observable<unknown>[] | Record<string, Observable<unknown>>;
|
|
75
|
+
|
|
76
|
+
function useStableNodes(nodeOrNodes: NodeInput): NodeInput {
|
|
77
|
+
const ref = useRef(nodeOrNodes);
|
|
78
|
+
|
|
79
|
+
// For arrays: check element-wise identity
|
|
80
|
+
if (Array.isArray(nodeOrNodes)) {
|
|
81
|
+
const prev = ref.current;
|
|
82
|
+
if (
|
|
83
|
+
!Array.isArray(prev) ||
|
|
84
|
+
prev.length !== nodeOrNodes.length ||
|
|
85
|
+
nodeOrNodes.some((n, i) => n !== (prev as Observable<unknown>[])[i])
|
|
86
|
+
) {
|
|
87
|
+
ref.current = nodeOrNodes;
|
|
88
|
+
}
|
|
89
|
+
return ref.current;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// For objects (not observable): check key-wise identity
|
|
93
|
+
if (!isObservable(nodeOrNodes)) {
|
|
94
|
+
const prev = ref.current;
|
|
95
|
+
if (isObservable(prev) || Array.isArray(prev)) {
|
|
96
|
+
ref.current = nodeOrNodes;
|
|
97
|
+
return ref.current;
|
|
98
|
+
}
|
|
99
|
+
const prevObj = prev as Record<string, Observable<unknown>>;
|
|
100
|
+
const currKeys = Object.keys(nodeOrNodes);
|
|
101
|
+
const prevKeys = Object.keys(prevObj);
|
|
102
|
+
if (
|
|
103
|
+
currKeys.length !== prevKeys.length ||
|
|
104
|
+
currKeys.some((k) => nodeOrNodes[k] !== prevObj[k])
|
|
105
|
+
) {
|
|
106
|
+
ref.current = nodeOrNodes;
|
|
107
|
+
}
|
|
108
|
+
return ref.current;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// For single observable: direct identity check
|
|
112
|
+
if (nodeOrNodes !== ref.current) {
|
|
113
|
+
ref.current = nodeOrNodes;
|
|
114
|
+
}
|
|
115
|
+
return ref.current;
|
|
116
|
+
}
|
|
117
|
+
|
|
62
118
|
/**
|
|
63
119
|
* Hook to subscribe to any Observable and get its current value.
|
|
64
120
|
* Re-renders the component whenever the observable emits a new value.
|
|
@@ -239,41 +295,62 @@ export function useSelect(
|
|
|
239
295
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
240
296
|
equalityFn: (a: any, b: any) => boolean = Object.is
|
|
241
297
|
): unknown {
|
|
298
|
+
// Use refs for selector and equalityFn so we always call the latest version
|
|
299
|
+
// without needing them as useMemo deps (which would recreate the observable).
|
|
300
|
+
const selectorRef = useRef(selector);
|
|
301
|
+
selectorRef.current = selector;
|
|
302
|
+
const equalityFnRef = useRef(equalityFn);
|
|
303
|
+
equalityFnRef.current = equalityFn;
|
|
304
|
+
|
|
305
|
+
// Stabilize the node identity across renders.
|
|
306
|
+
// Users pass inline arrays/objects like [store.a, store.b] or { a: store.a },
|
|
307
|
+
// which are new references each render. We extract the actual node references
|
|
308
|
+
// and only recreate the observable when the nodes themselves change.
|
|
309
|
+
const stableNodes = useStableNodes(nodeOrNodes);
|
|
310
|
+
|
|
242
311
|
// Determine the form and create the combined observable
|
|
243
312
|
const { combined$, getInitialValue } = useMemo(() => {
|
|
244
313
|
// Array form: [node1, node2, ...] - always requires selector
|
|
245
|
-
if (Array.isArray(
|
|
246
|
-
const nodes =
|
|
247
|
-
const sel = selector!; // selector is required for array form
|
|
314
|
+
if (Array.isArray(stableNodes)) {
|
|
315
|
+
const nodes = stableNodes as Observable<unknown>[];
|
|
248
316
|
return {
|
|
249
317
|
combined$: combineLatest(nodes).pipe(
|
|
250
|
-
|
|
251
|
-
|
|
318
|
+
// Deduplicate inputs so the selector only re-runs when an input actually changes.
|
|
319
|
+
// This prevents selectors that return new references (e.g. .sort(), .map())
|
|
320
|
+
// from causing infinite emission loops with the default Object.is equality.
|
|
321
|
+
distinctUntilChanged((a, b) =>
|
|
322
|
+
a.length === b.length && a.every((v, i) => Object.is(v, b[i]))
|
|
323
|
+
),
|
|
324
|
+
map((values) => selectorRef.current!(values)),
|
|
325
|
+
distinctUntilChanged((a, b) => equalityFnRef.current(a, b))
|
|
252
326
|
),
|
|
253
327
|
getInitialValue: (): unknown => {
|
|
254
328
|
const values = nodes.map((n) => (hasGet<unknown>(n) ? n.get() : undefined));
|
|
255
|
-
return
|
|
329
|
+
return selectorRef.current!(values);
|
|
256
330
|
},
|
|
257
331
|
};
|
|
258
332
|
}
|
|
259
333
|
|
|
260
334
|
// Object form: { a: node1, b: node2, ... } - always requires selector
|
|
261
|
-
if (!isObservable(
|
|
262
|
-
const obj =
|
|
335
|
+
if (!isObservable(stableNodes)) {
|
|
336
|
+
const obj = stableNodes as Record<string, Observable<unknown>>;
|
|
263
337
|
const keys = Object.keys(obj);
|
|
264
338
|
const observables = keys.map((k) => obj[k]);
|
|
265
|
-
const sel = selector!; // selector is required for object form
|
|
266
339
|
|
|
267
340
|
return {
|
|
268
341
|
combined$: combineLatest(observables).pipe(
|
|
342
|
+
// Deduplicate inputs so the selector only re-runs when an input actually changes.
|
|
343
|
+
distinctUntilChanged((a, b) =>
|
|
344
|
+
a.length === b.length && a.every((v, i) => Object.is(v, b[i]))
|
|
345
|
+
),
|
|
269
346
|
map((values) => {
|
|
270
347
|
const result: Record<string, unknown> = {};
|
|
271
348
|
keys.forEach((key, i) => {
|
|
272
349
|
result[key] = values[i];
|
|
273
350
|
});
|
|
274
|
-
return
|
|
351
|
+
return selectorRef.current!(result);
|
|
275
352
|
}),
|
|
276
|
-
distinctUntilChanged(
|
|
353
|
+
distinctUntilChanged((a, b) => equalityFnRef.current(a, b))
|
|
277
354
|
),
|
|
278
355
|
getInitialValue: (): unknown => {
|
|
279
356
|
const result: Record<string, unknown> = {};
|
|
@@ -281,24 +358,26 @@ export function useSelect(
|
|
|
281
358
|
const node = obj[key];
|
|
282
359
|
result[key] = hasGet<unknown>(node) ? node.get() : undefined;
|
|
283
360
|
});
|
|
284
|
-
return
|
|
361
|
+
return selectorRef.current!(result);
|
|
285
362
|
},
|
|
286
363
|
};
|
|
287
364
|
}
|
|
288
365
|
|
|
289
366
|
// Single node form - selector is optional
|
|
290
|
-
const node =
|
|
291
|
-
|
|
292
|
-
if (
|
|
367
|
+
const node = stableNodes as Observable<unknown>;
|
|
368
|
+
|
|
369
|
+
if (selectorRef.current) {
|
|
293
370
|
// With selector - apply transformation
|
|
294
371
|
return {
|
|
295
372
|
combined$: node.pipe(
|
|
296
|
-
|
|
297
|
-
distinctUntilChanged(
|
|
373
|
+
// Deduplicate inputs so the selector only re-runs when the input actually changes.
|
|
374
|
+
distinctUntilChanged(),
|
|
375
|
+
map((value) => selectorRef.current!(value)),
|
|
376
|
+
distinctUntilChanged((a, b) => equalityFnRef.current(a, b))
|
|
298
377
|
),
|
|
299
378
|
getInitialValue: (): unknown => {
|
|
300
379
|
if (hasGet<unknown>(node)) {
|
|
301
|
-
return
|
|
380
|
+
return selectorRef.current!(node.get());
|
|
302
381
|
}
|
|
303
382
|
return undefined;
|
|
304
383
|
},
|
|
@@ -307,7 +386,7 @@ export function useSelect(
|
|
|
307
386
|
// No selector - return raw value
|
|
308
387
|
return {
|
|
309
388
|
combined$: node.pipe(
|
|
310
|
-
distinctUntilChanged(
|
|
389
|
+
distinctUntilChanged((a, b) => equalityFnRef.current(a, b))
|
|
311
390
|
),
|
|
312
391
|
getInitialValue: (): unknown => {
|
|
313
392
|
if (hasGet<unknown>(node)) {
|
|
@@ -317,7 +396,8 @@ export function useSelect(
|
|
|
317
396
|
},
|
|
318
397
|
};
|
|
319
398
|
}
|
|
320
|
-
|
|
399
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
400
|
+
}, [stableNodes]);
|
|
321
401
|
|
|
322
402
|
// Ref to hold the current derived value
|
|
323
403
|
const valueRef = useRef<unknown>(getInitialValue());
|