jotai-state-tree 1.4.3 → 1.4.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/dist/{chunk-5OCZ6YLH.mjs → chunk-XA4ACZVI.mjs} +43 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +43 -0
- package/dist/index.mjs +1 -1
- package/dist/react.d.mts +2 -5
- package/dist/react.d.ts +2 -5
- package/dist/react.js +78 -136
- package/dist/react.mjs +42 -128
- package/dist/{undo-quCDYz_6.d.mts → undo-BSq-Pomp.d.mts} +5 -1
- package/dist/{undo-quCDYz_6.d.ts → undo-BSq-Pomp.d.ts} +5 -1
- package/package.json +1 -1
- package/src/react.ts +40 -153
- package/src/tree.ts +58 -1
|
@@ -128,6 +128,41 @@ var StateTreeNode = class {
|
|
|
128
128
|
this.$parent = parent ?? null;
|
|
129
129
|
this.$path = parent ? `${parent.$path}/${pathSegment}` : "";
|
|
130
130
|
this.valueAtom = atom(initialValue);
|
|
131
|
+
this.isAliveAtom = atom(true);
|
|
132
|
+
this.snapshotAtom = atom((get) => {
|
|
133
|
+
const value = get(this.valueAtom);
|
|
134
|
+
if (this.$type._kind === "model") {
|
|
135
|
+
const res = {};
|
|
136
|
+
for (const [key, childNode] of this.children.entries()) {
|
|
137
|
+
res[key] = get(childNode.snapshotAtom);
|
|
138
|
+
}
|
|
139
|
+
if (value && typeof value === "object") {
|
|
140
|
+
for (const key of Object.keys(value)) {
|
|
141
|
+
if (!this.children.has(key)) {
|
|
142
|
+
res[key] = value[key];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return this.postProcessor ? this.postProcessor(res) : res;
|
|
147
|
+
}
|
|
148
|
+
if (this.$type._kind === "array") {
|
|
149
|
+
const res = [];
|
|
150
|
+
const childrenKeys = Array.from(this.children.keys()).sort((a, b) => Number(a) - Number(b));
|
|
151
|
+
for (const key of childrenKeys) {
|
|
152
|
+
const childNode = this.children.get(key);
|
|
153
|
+
res.push(get(childNode.snapshotAtom));
|
|
154
|
+
}
|
|
155
|
+
return res;
|
|
156
|
+
}
|
|
157
|
+
if (this.$type._kind === "map") {
|
|
158
|
+
const res = {};
|
|
159
|
+
for (const [key, childNode] of this.children.entries()) {
|
|
160
|
+
res[key] = get(childNode.snapshotAtom);
|
|
161
|
+
}
|
|
162
|
+
return res;
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
});
|
|
131
166
|
nodeRegistry.set(this.$id, { node: new WeakRef(this), instance: null });
|
|
132
167
|
nodeFinalizationRegistry.register(this, this.$id, this);
|
|
133
168
|
}
|
|
@@ -319,6 +354,10 @@ var StateTreeNode = class {
|
|
|
319
354
|
this.children.clear();
|
|
320
355
|
this.unregisterIdentifier();
|
|
321
356
|
this.$isAlive = false;
|
|
357
|
+
try {
|
|
358
|
+
globalStore.set(this.isAliveAtom, false);
|
|
359
|
+
} catch (e) {
|
|
360
|
+
}
|
|
322
361
|
notifyLifecycleChange(this, false);
|
|
323
362
|
nodeRegistry.delete(this.$id);
|
|
324
363
|
nodeFinalizationRegistry.unregister(this);
|
|
@@ -495,6 +534,10 @@ function clearAllRegistries() {
|
|
|
495
534
|
const node = entry.node.deref();
|
|
496
535
|
if (node) {
|
|
497
536
|
node.$isAlive = false;
|
|
537
|
+
try {
|
|
538
|
+
globalStore.set(node.isAliveAtom, false);
|
|
539
|
+
} catch (e) {
|
|
540
|
+
}
|
|
498
541
|
}
|
|
499
542
|
}
|
|
500
543
|
nodeRegistry.clear();
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as ISimpleType, a as IType, b as IIdentifierType, c as IIdentifierNumberType, d as ILiteralType, e as IEnumerationType, f as IFrozenType, M as ModelProperties, g as IModelType, h as MixinConfig, i as IMixin, j as IAnyType, k as IArrayType, l as IMapType, m as IOptionalType, n as IMaybeType, o as IMaybeNullType, p as IUnionType, U as UnionOptions, q as ILateType, r as IAnyModelType, R as ReferenceOptions, s as IReferenceType, t as ISafeReferenceType, u as IRefinementType, v as IDisposer, S as SnapshotIn, w as Instance } from './undo-
|
|
2
|
-
export { L as CustomTypeOptions, aG as IActionRecorder, aH as IActionRecording, C as IAnyComplexType, D as IAnyMixin, aE as IHistoryEntry, F as IJsonPatch, y as IMSTArray, z as IMSTMap, G as IReversibleJsonPatch, x as IStateTreeNode, aF as ITimeTravelManager, aC as IUndoManager, aD as IUndoManagerOptions, H as IValidationContext, K as IValidationError, J as IValidationResult, A as ModelInstance, E as ModelSelf, B as SnapshotOut, T as applyPatch, O as applySnapshot, aw as cleanupStaleEntries, ax as clearAllRegistries, aa as clone, aq as cloneDeep, aB as createActionRecorder, aA as createTimeTravelManager, az as createUndoManager, a8 as destroy, a9 as detach, am as findAll, an as findFirst, as as freeze, a2 as getEnv, ag as getGlobalStore, a4 as getIdentifier, af as getMembers, ar as getOrCreatePath, Y as getParent, $ as getParentOfType, a0 as getPath, a1 as getPathParts, av as getRegistryStats, aj as getRelativePath, X as getRoot, N as getSnapshot, ap as getTreeStats, a3 as getType, _ as hasParent, al as haveSameRoot, a5 as isAlive, ak as isAncestor, at as isFrozen, a6 as isRoot, a7 as isStateTreeNode, ao as isValidReference, W as onAction, ay as onLifecycleChange, Q as onPatch, P as onSnapshot, V as recordPatches, ai as resetGlobalStore, ae as resolveIdentifier, ac as resolvePath, ah as setGlobalStore, Z as tryGetParent, ad as tryResolve, au as unfreeze, ab as walk } from './undo-
|
|
1
|
+
import { I as ISimpleType, a as IType, b as IIdentifierType, c as IIdentifierNumberType, d as ILiteralType, e as IEnumerationType, f as IFrozenType, M as ModelProperties, g as IModelType, h as MixinConfig, i as IMixin, j as IAnyType, k as IArrayType, l as IMapType, m as IOptionalType, n as IMaybeType, o as IMaybeNullType, p as IUnionType, U as UnionOptions, q as ILateType, r as IAnyModelType, R as ReferenceOptions, s as IReferenceType, t as ISafeReferenceType, u as IRefinementType, v as IDisposer, S as SnapshotIn, w as Instance } from './undo-BSq-Pomp.mjs';
|
|
2
|
+
export { L as CustomTypeOptions, aG as IActionRecorder, aH as IActionRecording, C as IAnyComplexType, D as IAnyMixin, aE as IHistoryEntry, F as IJsonPatch, y as IMSTArray, z as IMSTMap, G as IReversibleJsonPatch, x as IStateTreeNode, aF as ITimeTravelManager, aC as IUndoManager, aD as IUndoManagerOptions, H as IValidationContext, K as IValidationError, J as IValidationResult, A as ModelInstance, E as ModelSelf, B as SnapshotOut, T as applyPatch, O as applySnapshot, aw as cleanupStaleEntries, ax as clearAllRegistries, aa as clone, aq as cloneDeep, aB as createActionRecorder, aA as createTimeTravelManager, az as createUndoManager, a8 as destroy, a9 as detach, am as findAll, an as findFirst, as as freeze, a2 as getEnv, ag as getGlobalStore, a4 as getIdentifier, af as getMembers, ar as getOrCreatePath, Y as getParent, $ as getParentOfType, a0 as getPath, a1 as getPathParts, av as getRegistryStats, aj as getRelativePath, X as getRoot, N as getSnapshot, ap as getTreeStats, a3 as getType, _ as hasParent, al as haveSameRoot, a5 as isAlive, ak as isAncestor, at as isFrozen, a6 as isRoot, a7 as isStateTreeNode, ao as isValidReference, W as onAction, ay as onLifecycleChange, Q as onPatch, P as onSnapshot, V as recordPatches, ai as resetGlobalStore, ae as resolveIdentifier, ac as resolvePath, ah as setGlobalStore, Z as tryGetParent, ad as tryResolve, au as unfreeze, ab as walk } from './undo-BSq-Pomp.mjs';
|
|
3
3
|
import 'jotai/vanilla/internals';
|
|
4
4
|
import 'jotai';
|
|
5
5
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as ISimpleType, a as IType, b as IIdentifierType, c as IIdentifierNumberType, d as ILiteralType, e as IEnumerationType, f as IFrozenType, M as ModelProperties, g as IModelType, h as MixinConfig, i as IMixin, j as IAnyType, k as IArrayType, l as IMapType, m as IOptionalType, n as IMaybeType, o as IMaybeNullType, p as IUnionType, U as UnionOptions, q as ILateType, r as IAnyModelType, R as ReferenceOptions, s as IReferenceType, t as ISafeReferenceType, u as IRefinementType, v as IDisposer, S as SnapshotIn, w as Instance } from './undo-
|
|
2
|
-
export { L as CustomTypeOptions, aG as IActionRecorder, aH as IActionRecording, C as IAnyComplexType, D as IAnyMixin, aE as IHistoryEntry, F as IJsonPatch, y as IMSTArray, z as IMSTMap, G as IReversibleJsonPatch, x as IStateTreeNode, aF as ITimeTravelManager, aC as IUndoManager, aD as IUndoManagerOptions, H as IValidationContext, K as IValidationError, J as IValidationResult, A as ModelInstance, E as ModelSelf, B as SnapshotOut, T as applyPatch, O as applySnapshot, aw as cleanupStaleEntries, ax as clearAllRegistries, aa as clone, aq as cloneDeep, aB as createActionRecorder, aA as createTimeTravelManager, az as createUndoManager, a8 as destroy, a9 as detach, am as findAll, an as findFirst, as as freeze, a2 as getEnv, ag as getGlobalStore, a4 as getIdentifier, af as getMembers, ar as getOrCreatePath, Y as getParent, $ as getParentOfType, a0 as getPath, a1 as getPathParts, av as getRegistryStats, aj as getRelativePath, X as getRoot, N as getSnapshot, ap as getTreeStats, a3 as getType, _ as hasParent, al as haveSameRoot, a5 as isAlive, ak as isAncestor, at as isFrozen, a6 as isRoot, a7 as isStateTreeNode, ao as isValidReference, W as onAction, ay as onLifecycleChange, Q as onPatch, P as onSnapshot, V as recordPatches, ai as resetGlobalStore, ae as resolveIdentifier, ac as resolvePath, ah as setGlobalStore, Z as tryGetParent, ad as tryResolve, au as unfreeze, ab as walk } from './undo-
|
|
1
|
+
import { I as ISimpleType, a as IType, b as IIdentifierType, c as IIdentifierNumberType, d as ILiteralType, e as IEnumerationType, f as IFrozenType, M as ModelProperties, g as IModelType, h as MixinConfig, i as IMixin, j as IAnyType, k as IArrayType, l as IMapType, m as IOptionalType, n as IMaybeType, o as IMaybeNullType, p as IUnionType, U as UnionOptions, q as ILateType, r as IAnyModelType, R as ReferenceOptions, s as IReferenceType, t as ISafeReferenceType, u as IRefinementType, v as IDisposer, S as SnapshotIn, w as Instance } from './undo-BSq-Pomp.js';
|
|
2
|
+
export { L as CustomTypeOptions, aG as IActionRecorder, aH as IActionRecording, C as IAnyComplexType, D as IAnyMixin, aE as IHistoryEntry, F as IJsonPatch, y as IMSTArray, z as IMSTMap, G as IReversibleJsonPatch, x as IStateTreeNode, aF as ITimeTravelManager, aC as IUndoManager, aD as IUndoManagerOptions, H as IValidationContext, K as IValidationError, J as IValidationResult, A as ModelInstance, E as ModelSelf, B as SnapshotOut, T as applyPatch, O as applySnapshot, aw as cleanupStaleEntries, ax as clearAllRegistries, aa as clone, aq as cloneDeep, aB as createActionRecorder, aA as createTimeTravelManager, az as createUndoManager, a8 as destroy, a9 as detach, am as findAll, an as findFirst, as as freeze, a2 as getEnv, ag as getGlobalStore, a4 as getIdentifier, af as getMembers, ar as getOrCreatePath, Y as getParent, $ as getParentOfType, a0 as getPath, a1 as getPathParts, av as getRegistryStats, aj as getRelativePath, X as getRoot, N as getSnapshot, ap as getTreeStats, a3 as getType, _ as hasParent, al as haveSameRoot, a5 as isAlive, ak as isAncestor, at as isFrozen, a6 as isRoot, a7 as isStateTreeNode, ao as isValidReference, W as onAction, ay as onLifecycleChange, Q as onPatch, P as onSnapshot, V as recordPatches, ai as resetGlobalStore, ae as resolveIdentifier, ac as resolvePath, ah as setGlobalStore, Z as tryGetParent, ad as tryResolve, au as unfreeze, ab as walk } from './undo-BSq-Pomp.js';
|
|
3
3
|
import 'jotai/vanilla/internals';
|
|
4
4
|
import 'jotai';
|
|
5
5
|
|
package/dist/index.js
CHANGED
|
@@ -559,6 +559,41 @@ var StateTreeNode = class {
|
|
|
559
559
|
this.$parent = parent ?? null;
|
|
560
560
|
this.$path = parent ? `${parent.$path}/${pathSegment}` : "";
|
|
561
561
|
this.valueAtom = (0, import_jotai.atom)(initialValue);
|
|
562
|
+
this.isAliveAtom = (0, import_jotai.atom)(true);
|
|
563
|
+
this.snapshotAtom = (0, import_jotai.atom)((get) => {
|
|
564
|
+
const value = get(this.valueAtom);
|
|
565
|
+
if (this.$type._kind === "model") {
|
|
566
|
+
const res = {};
|
|
567
|
+
for (const [key, childNode] of this.children.entries()) {
|
|
568
|
+
res[key] = get(childNode.snapshotAtom);
|
|
569
|
+
}
|
|
570
|
+
if (value && typeof value === "object") {
|
|
571
|
+
for (const key of Object.keys(value)) {
|
|
572
|
+
if (!this.children.has(key)) {
|
|
573
|
+
res[key] = value[key];
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return this.postProcessor ? this.postProcessor(res) : res;
|
|
578
|
+
}
|
|
579
|
+
if (this.$type._kind === "array") {
|
|
580
|
+
const res = [];
|
|
581
|
+
const childrenKeys = Array.from(this.children.keys()).sort((a, b) => Number(a) - Number(b));
|
|
582
|
+
for (const key of childrenKeys) {
|
|
583
|
+
const childNode = this.children.get(key);
|
|
584
|
+
res.push(get(childNode.snapshotAtom));
|
|
585
|
+
}
|
|
586
|
+
return res;
|
|
587
|
+
}
|
|
588
|
+
if (this.$type._kind === "map") {
|
|
589
|
+
const res = {};
|
|
590
|
+
for (const [key, childNode] of this.children.entries()) {
|
|
591
|
+
res[key] = get(childNode.snapshotAtom);
|
|
592
|
+
}
|
|
593
|
+
return res;
|
|
594
|
+
}
|
|
595
|
+
return value;
|
|
596
|
+
});
|
|
562
597
|
nodeRegistry.set(this.$id, { node: new WeakRef(this), instance: null });
|
|
563
598
|
nodeFinalizationRegistry.register(this, this.$id, this);
|
|
564
599
|
}
|
|
@@ -750,6 +785,10 @@ var StateTreeNode = class {
|
|
|
750
785
|
this.children.clear();
|
|
751
786
|
this.unregisterIdentifier();
|
|
752
787
|
this.$isAlive = false;
|
|
788
|
+
try {
|
|
789
|
+
globalStore.set(this.isAliveAtom, false);
|
|
790
|
+
} catch (e) {
|
|
791
|
+
}
|
|
753
792
|
notifyLifecycleChange(this, false);
|
|
754
793
|
nodeRegistry.delete(this.$id);
|
|
755
794
|
nodeFinalizationRegistry.unregister(this);
|
|
@@ -926,6 +965,10 @@ function clearAllRegistries() {
|
|
|
926
965
|
const node = entry.node.deref();
|
|
927
966
|
if (node) {
|
|
928
967
|
node.$isAlive = false;
|
|
968
|
+
try {
|
|
969
|
+
globalStore.set(node.isAliveAtom, false);
|
|
970
|
+
} catch (e) {
|
|
971
|
+
}
|
|
929
972
|
}
|
|
930
973
|
}
|
|
931
974
|
nodeRegistry.clear();
|
package/dist/index.mjs
CHANGED
package/dist/react.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { ComponentType, FC, ReactNode } from 'react';
|
|
2
|
-
import { ag as getGlobalStore, aD as IUndoManagerOptions, aC as IUndoManager, aF as ITimeTravelManager } from './undo-
|
|
3
|
-
export { aI as hasStateTreeNode } from './undo-
|
|
2
|
+
import { ag as getGlobalStore, aD as IUndoManagerOptions, aC as IUndoManager, aF as ITimeTravelManager } from './undo-BSq-Pomp.mjs';
|
|
3
|
+
export { aI as hasStateTreeNode } from './undo-BSq-Pomp.mjs';
|
|
4
4
|
import 'jotai/vanilla/internals';
|
|
5
5
|
import 'jotai';
|
|
6
6
|
|
|
@@ -108,9 +108,6 @@ declare function createStoreContext<T>(): {
|
|
|
108
108
|
useIsAlive: () => boolean;
|
|
109
109
|
Context: React.Context<T | null>;
|
|
110
110
|
};
|
|
111
|
-
/**
|
|
112
|
-
* Hook that returns the current snapshot and re-renders on changes.
|
|
113
|
-
*/
|
|
114
111
|
declare function useSnapshot<T>(target: unknown): T;
|
|
115
112
|
/**
|
|
116
113
|
* Hook to watch specific paths in a state tree
|
package/dist/react.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { ComponentType, FC, ReactNode } from 'react';
|
|
2
|
-
import { ag as getGlobalStore, aD as IUndoManagerOptions, aC as IUndoManager, aF as ITimeTravelManager } from './undo-
|
|
3
|
-
export { aI as hasStateTreeNode } from './undo-
|
|
2
|
+
import { ag as getGlobalStore, aD as IUndoManagerOptions, aC as IUndoManager, aF as ITimeTravelManager } from './undo-BSq-Pomp.js';
|
|
3
|
+
export { aI as hasStateTreeNode } from './undo-BSq-Pomp.js';
|
|
4
4
|
import 'jotai/vanilla/internals';
|
|
5
5
|
import 'jotai';
|
|
6
6
|
|
|
@@ -108,9 +108,6 @@ declare function createStoreContext<T>(): {
|
|
|
108
108
|
useIsAlive: () => boolean;
|
|
109
109
|
Context: React.Context<T | null>;
|
|
110
110
|
};
|
|
111
|
-
/**
|
|
112
|
-
* Hook that returns the current snapshot and re-renders on changes.
|
|
113
|
-
*/
|
|
114
111
|
declare function useSnapshot<T>(target: unknown): T;
|
|
115
112
|
/**
|
|
116
113
|
* Hook to watch specific paths in a state tree
|
package/dist/react.js
CHANGED
|
@@ -109,17 +109,6 @@ function generateNodeId() {
|
|
|
109
109
|
return `node_${++nodeIdCounter}_${Date.now().toString(36)}`;
|
|
110
110
|
}
|
|
111
111
|
var lifecycleListeners = /* @__PURE__ */ new WeakMap();
|
|
112
|
-
function onLifecycleChange(node, listener) {
|
|
113
|
-
let listeners = lifecycleListeners.get(node);
|
|
114
|
-
if (!listeners) {
|
|
115
|
-
listeners = /* @__PURE__ */ new Set();
|
|
116
|
-
lifecycleListeners.set(node, listeners);
|
|
117
|
-
}
|
|
118
|
-
listeners.add(listener);
|
|
119
|
-
return () => {
|
|
120
|
-
listeners?.delete(listener);
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
112
|
function notifyLifecycleChange(node, isAlive) {
|
|
124
113
|
const listeners = lifecycleListeners.get(node);
|
|
125
114
|
if (listeners) {
|
|
@@ -180,6 +169,41 @@ var StateTreeNode = class {
|
|
|
180
169
|
this.$parent = parent ?? null;
|
|
181
170
|
this.$path = parent ? `${parent.$path}/${pathSegment}` : "";
|
|
182
171
|
this.valueAtom = (0, import_jotai.atom)(initialValue);
|
|
172
|
+
this.isAliveAtom = (0, import_jotai.atom)(true);
|
|
173
|
+
this.snapshotAtom = (0, import_jotai.atom)((get) => {
|
|
174
|
+
const value = get(this.valueAtom);
|
|
175
|
+
if (this.$type._kind === "model") {
|
|
176
|
+
const res = {};
|
|
177
|
+
for (const [key, childNode] of this.children.entries()) {
|
|
178
|
+
res[key] = get(childNode.snapshotAtom);
|
|
179
|
+
}
|
|
180
|
+
if (value && typeof value === "object") {
|
|
181
|
+
for (const key of Object.keys(value)) {
|
|
182
|
+
if (!this.children.has(key)) {
|
|
183
|
+
res[key] = value[key];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return this.postProcessor ? this.postProcessor(res) : res;
|
|
188
|
+
}
|
|
189
|
+
if (this.$type._kind === "array") {
|
|
190
|
+
const res = [];
|
|
191
|
+
const childrenKeys = Array.from(this.children.keys()).sort((a, b) => Number(a) - Number(b));
|
|
192
|
+
for (const key of childrenKeys) {
|
|
193
|
+
const childNode = this.children.get(key);
|
|
194
|
+
res.push(get(childNode.snapshotAtom));
|
|
195
|
+
}
|
|
196
|
+
return res;
|
|
197
|
+
}
|
|
198
|
+
if (this.$type._kind === "map") {
|
|
199
|
+
const res = {};
|
|
200
|
+
for (const [key, childNode] of this.children.entries()) {
|
|
201
|
+
res[key] = get(childNode.snapshotAtom);
|
|
202
|
+
}
|
|
203
|
+
return res;
|
|
204
|
+
}
|
|
205
|
+
return value;
|
|
206
|
+
});
|
|
183
207
|
nodeRegistry.set(this.$id, { node: new WeakRef(this), instance: null });
|
|
184
208
|
nodeFinalizationRegistry.register(this, this.$id, this);
|
|
185
209
|
}
|
|
@@ -371,6 +395,10 @@ var StateTreeNode = class {
|
|
|
371
395
|
this.children.clear();
|
|
372
396
|
this.unregisterIdentifier();
|
|
373
397
|
this.$isAlive = false;
|
|
398
|
+
try {
|
|
399
|
+
globalStore.set(this.isAliveAtom, false);
|
|
400
|
+
} catch (e) {
|
|
401
|
+
}
|
|
374
402
|
notifyLifecycleChange(this, false);
|
|
375
403
|
nodeRegistry.delete(this.$id);
|
|
376
404
|
nodeFinalizationRegistry.unregister(this);
|
|
@@ -504,10 +532,6 @@ function applySnapshot(target, snapshot) {
|
|
|
504
532
|
setIsApplyingSnapshotOrPatch(wasApplying);
|
|
505
533
|
}
|
|
506
534
|
}
|
|
507
|
-
function onSnapshot(target, listener) {
|
|
508
|
-
const node = getStateTreeNode(target);
|
|
509
|
-
return node.onSnapshot(listener);
|
|
510
|
-
}
|
|
511
535
|
function onPatch(target, listener) {
|
|
512
536
|
const node = getStateTreeNode(target);
|
|
513
537
|
return node.onPatch(listener);
|
|
@@ -1007,60 +1031,19 @@ function useObserver(fn) {
|
|
|
1007
1031
|
}
|
|
1008
1032
|
}
|
|
1009
1033
|
function useLocalObservable(initializer, dependencies = []) {
|
|
1010
|
-
const
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
storeRef.current = initializer();
|
|
1015
|
-
if (hasStateTreeNode(storeRef.current)) {
|
|
1016
|
-
disposerRef.current = onSnapshot(storeRef.current, () => {
|
|
1017
|
-
forceUpdate({});
|
|
1018
|
-
});
|
|
1019
|
-
}
|
|
1034
|
+
const store = (0, import_react.useMemo)(initializer, dependencies);
|
|
1035
|
+
if (hasStateTreeNode(store)) {
|
|
1036
|
+
const node = getStateTreeNode(store);
|
|
1037
|
+
(0, import_jotai3.useAtomValue)(node.snapshotAtom, { store: getGlobalStore() });
|
|
1020
1038
|
}
|
|
1021
|
-
|
|
1022
|
-
return () => {
|
|
1023
|
-
disposerRef.current?.();
|
|
1024
|
-
};
|
|
1025
|
-
}, []);
|
|
1026
|
-
(0, import_react.useEffect)(() => {
|
|
1027
|
-
if (dependencies.length > 0) {
|
|
1028
|
-
disposerRef.current?.();
|
|
1029
|
-
storeRef.current = initializer();
|
|
1030
|
-
if (hasStateTreeNode(storeRef.current)) {
|
|
1031
|
-
disposerRef.current = onSnapshot(storeRef.current, () => {
|
|
1032
|
-
forceUpdate({});
|
|
1033
|
-
});
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
}, dependencies);
|
|
1037
|
-
return storeRef.current;
|
|
1039
|
+
return store;
|
|
1038
1040
|
}
|
|
1039
1041
|
function useSyncedStore(store) {
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
};
|
|
1046
|
-
}
|
|
1047
|
-
return onSnapshot(store, () => {
|
|
1048
|
-
snapshotRef.current = getSnapshot(store);
|
|
1049
|
-
callback();
|
|
1050
|
-
});
|
|
1051
|
-
},
|
|
1052
|
-
[store]
|
|
1053
|
-
);
|
|
1054
|
-
const getSnapshotValue = (0, import_react.useCallback)(() => {
|
|
1055
|
-
if (!hasStateTreeNode(store)) {
|
|
1056
|
-
return null;
|
|
1057
|
-
}
|
|
1058
|
-
if (snapshotRef.current === null) {
|
|
1059
|
-
snapshotRef.current = getSnapshot(store);
|
|
1060
|
-
}
|
|
1061
|
-
return snapshotRef.current;
|
|
1062
|
-
}, [store]);
|
|
1063
|
-
(0, import_react.useSyncExternalStore)(subscribe, getSnapshotValue, getSnapshotValue);
|
|
1042
|
+
if (!hasStateTreeNode(store)) {
|
|
1043
|
+
return store;
|
|
1044
|
+
}
|
|
1045
|
+
const node = getStateTreeNode(store);
|
|
1046
|
+
(0, import_jotai3.useAtomValue)(node.snapshotAtom, { store: getGlobalStore() });
|
|
1064
1047
|
return store;
|
|
1065
1048
|
}
|
|
1066
1049
|
var StoreContext = import_react.default.createContext(
|
|
@@ -1084,20 +1067,12 @@ function useStore() {
|
|
|
1084
1067
|
}
|
|
1085
1068
|
function useStoreSnapshot(selector) {
|
|
1086
1069
|
const store = useStore();
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
if (hasStateTreeNode(store)) {
|
|
1090
|
-
return onSnapshot(store, () => {
|
|
1091
|
-
forceUpdate({});
|
|
1092
|
-
});
|
|
1093
|
-
}
|
|
1094
|
-
return () => {
|
|
1095
|
-
};
|
|
1096
|
-
}, [store]);
|
|
1097
|
-
if (selector) {
|
|
1098
|
-
return selector(store);
|
|
1070
|
+
if (!hasStateTreeNode(store)) {
|
|
1071
|
+
return store;
|
|
1099
1072
|
}
|
|
1100
|
-
|
|
1073
|
+
const node = getStateTreeNode(store);
|
|
1074
|
+
(0, import_jotai3.useAtomValue)(node.snapshotAtom, { store: getGlobalStore() });
|
|
1075
|
+
return selector ? selector(store) : store;
|
|
1101
1076
|
}
|
|
1102
1077
|
function createStoreContext() {
|
|
1103
1078
|
const Context = import_react.default.createContext(null);
|
|
@@ -1118,20 +1093,12 @@ function createStoreContext() {
|
|
|
1118
1093
|
}
|
|
1119
1094
|
function useTypedStoreSnapshot(selector) {
|
|
1120
1095
|
const store = useTypedStore();
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
if (hasStateTreeNode(store)) {
|
|
1124
|
-
return onSnapshot(store, () => {
|
|
1125
|
-
forceUpdate({});
|
|
1126
|
-
});
|
|
1127
|
-
}
|
|
1128
|
-
return () => {
|
|
1129
|
-
};
|
|
1130
|
-
}, [store]);
|
|
1131
|
-
if (selector) {
|
|
1132
|
-
return selector(store);
|
|
1096
|
+
if (!hasStateTreeNode(store)) {
|
|
1097
|
+
return store;
|
|
1133
1098
|
}
|
|
1134
|
-
|
|
1099
|
+
const node = getStateTreeNode(store);
|
|
1100
|
+
(0, import_jotai3.useAtomValue)(node.snapshotAtom, { store: getGlobalStore() });
|
|
1101
|
+
return selector ? selector(store) : store;
|
|
1135
1102
|
}
|
|
1136
1103
|
function useTypedIsAlive() {
|
|
1137
1104
|
const store = useTypedStore();
|
|
@@ -1146,47 +1113,33 @@ function createStoreContext() {
|
|
|
1146
1113
|
};
|
|
1147
1114
|
}
|
|
1148
1115
|
function useSnapshot(target) {
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
return disposer;
|
|
1155
|
-
}, [target]);
|
|
1156
|
-
return snapshot;
|
|
1116
|
+
if (!hasStateTreeNode(target)) {
|
|
1117
|
+
return target;
|
|
1118
|
+
}
|
|
1119
|
+
const node = getStateTreeNode(target);
|
|
1120
|
+
return (0, import_jotai3.useAtomValue)(node.snapshotAtom, { store: getGlobalStore() });
|
|
1157
1121
|
}
|
|
1158
1122
|
function useWatchPath(target, path, defaultValue) {
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
} else {
|
|
1167
|
-
return defaultValue;
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
return current;
|
|
1171
|
-
});
|
|
1172
|
-
(0, import_react.useEffect)(() => {
|
|
1173
|
-
const disposer = onSnapshot(target, (newSnapshot) => {
|
|
1174
|
-
const snapshot = newSnapshot;
|
|
1123
|
+
if (!hasStateTreeNode(target)) {
|
|
1124
|
+
return defaultValue;
|
|
1125
|
+
}
|
|
1126
|
+
const node = getStateTreeNode(target);
|
|
1127
|
+
const pathAtom = (0, import_react.useMemo)(() => {
|
|
1128
|
+
return (0, import_jotai3.atom)((get) => {
|
|
1129
|
+
const snapshot = get(node.snapshotAtom);
|
|
1175
1130
|
const parts = path.split(".");
|
|
1176
1131
|
let current = snapshot;
|
|
1177
1132
|
for (const part of parts) {
|
|
1178
1133
|
if (current && typeof current === "object" && part in current) {
|
|
1179
1134
|
current = current[part];
|
|
1180
1135
|
} else {
|
|
1181
|
-
|
|
1182
|
-
return;
|
|
1136
|
+
return defaultValue;
|
|
1183
1137
|
}
|
|
1184
1138
|
}
|
|
1185
|
-
|
|
1139
|
+
return current;
|
|
1186
1140
|
});
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
return value;
|
|
1141
|
+
}, [node, path, defaultValue]);
|
|
1142
|
+
return (0, import_jotai3.useAtomValue)(pathAtom, { store: getGlobalStore() });
|
|
1190
1143
|
}
|
|
1191
1144
|
function usePatches(target, callback) {
|
|
1192
1145
|
(0, import_react.useEffect)(() => {
|
|
@@ -1223,20 +1176,9 @@ function scheduleUpdate(update) {
|
|
|
1223
1176
|
}
|
|
1224
1177
|
}
|
|
1225
1178
|
function useIsAlive(target) {
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
});
|
|
1230
|
-
(0, import_react.useEffect)(() => {
|
|
1231
|
-
if (!hasStateTreeNode(target)) return;
|
|
1232
|
-
const node = getStateTreeNode(target);
|
|
1233
|
-
setIsAlive(node.$isAlive);
|
|
1234
|
-
const disposer = onLifecycleChange(node, (alive) => {
|
|
1235
|
-
setIsAlive(alive);
|
|
1236
|
-
});
|
|
1237
|
-
return disposer;
|
|
1238
|
-
}, [target]);
|
|
1239
|
-
return isAlive;
|
|
1179
|
+
if (!hasStateTreeNode(target)) return false;
|
|
1180
|
+
const node = getStateTreeNode(target);
|
|
1181
|
+
return (0, import_jotai3.useAtomValue)(node.isAliveAtom, { store: getGlobalStore() });
|
|
1240
1182
|
}
|
|
1241
1183
|
function useCleanup(cleanupFn) {
|
|
1242
1184
|
const cleanupRef = (0, import_react.useRef)(cleanupFn);
|
package/dist/react.mjs
CHANGED
|
@@ -5,15 +5,12 @@ import {
|
|
|
5
5
|
getGlobalStore,
|
|
6
6
|
getIsApplyingSnapshotOrPatch,
|
|
7
7
|
getOrCreateHistoryTracker,
|
|
8
|
-
getSnapshot,
|
|
9
8
|
getStateTreeNode,
|
|
10
9
|
hasStateTreeNode,
|
|
11
|
-
onLifecycleChange,
|
|
12
10
|
onPatch,
|
|
13
|
-
onSnapshot,
|
|
14
11
|
setActiveTrackingFn,
|
|
15
12
|
setIsApplyingSnapshotOrPatch
|
|
16
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-XA4ACZVI.mjs";
|
|
17
14
|
|
|
18
15
|
// src/react.ts
|
|
19
16
|
import React, {
|
|
@@ -22,11 +19,10 @@ import React, {
|
|
|
22
19
|
useMemo,
|
|
23
20
|
useRef,
|
|
24
21
|
forwardRef,
|
|
25
|
-
memo
|
|
26
|
-
useCallback,
|
|
27
|
-
useSyncExternalStore
|
|
22
|
+
memo
|
|
28
23
|
} from "react";
|
|
29
24
|
import {
|
|
25
|
+
atom,
|
|
30
26
|
useAtomValue
|
|
31
27
|
} from "jotai";
|
|
32
28
|
import { useHydrateAtoms } from "jotai/utils";
|
|
@@ -116,60 +112,19 @@ function useObserver(fn) {
|
|
|
116
112
|
}
|
|
117
113
|
}
|
|
118
114
|
function useLocalObservable(initializer, dependencies = []) {
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
storeRef.current = initializer();
|
|
124
|
-
if (hasStateTreeNode(storeRef.current)) {
|
|
125
|
-
disposerRef.current = onSnapshot(storeRef.current, () => {
|
|
126
|
-
forceUpdate({});
|
|
127
|
-
});
|
|
128
|
-
}
|
|
115
|
+
const store = useMemo(initializer, dependencies);
|
|
116
|
+
if (hasStateTreeNode(store)) {
|
|
117
|
+
const node = getStateTreeNode(store);
|
|
118
|
+
useAtomValue(node.snapshotAtom, { store: getGlobalStore() });
|
|
129
119
|
}
|
|
130
|
-
|
|
131
|
-
return () => {
|
|
132
|
-
disposerRef.current?.();
|
|
133
|
-
};
|
|
134
|
-
}, []);
|
|
135
|
-
useEffect(() => {
|
|
136
|
-
if (dependencies.length > 0) {
|
|
137
|
-
disposerRef.current?.();
|
|
138
|
-
storeRef.current = initializer();
|
|
139
|
-
if (hasStateTreeNode(storeRef.current)) {
|
|
140
|
-
disposerRef.current = onSnapshot(storeRef.current, () => {
|
|
141
|
-
forceUpdate({});
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}, dependencies);
|
|
146
|
-
return storeRef.current;
|
|
120
|
+
return store;
|
|
147
121
|
}
|
|
148
122
|
function useSyncedStore(store) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
return onSnapshot(store, () => {
|
|
157
|
-
snapshotRef.current = getSnapshot(store);
|
|
158
|
-
callback();
|
|
159
|
-
});
|
|
160
|
-
},
|
|
161
|
-
[store]
|
|
162
|
-
);
|
|
163
|
-
const getSnapshotValue = useCallback(() => {
|
|
164
|
-
if (!hasStateTreeNode(store)) {
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
if (snapshotRef.current === null) {
|
|
168
|
-
snapshotRef.current = getSnapshot(store);
|
|
169
|
-
}
|
|
170
|
-
return snapshotRef.current;
|
|
171
|
-
}, [store]);
|
|
172
|
-
useSyncExternalStore(subscribe, getSnapshotValue, getSnapshotValue);
|
|
123
|
+
if (!hasStateTreeNode(store)) {
|
|
124
|
+
return store;
|
|
125
|
+
}
|
|
126
|
+
const node = getStateTreeNode(store);
|
|
127
|
+
useAtomValue(node.snapshotAtom, { store: getGlobalStore() });
|
|
173
128
|
return store;
|
|
174
129
|
}
|
|
175
130
|
var StoreContext = React.createContext(
|
|
@@ -193,20 +148,12 @@ function useStore() {
|
|
|
193
148
|
}
|
|
194
149
|
function useStoreSnapshot(selector) {
|
|
195
150
|
const store = useStore();
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (hasStateTreeNode(store)) {
|
|
199
|
-
return onSnapshot(store, () => {
|
|
200
|
-
forceUpdate({});
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
return () => {
|
|
204
|
-
};
|
|
205
|
-
}, [store]);
|
|
206
|
-
if (selector) {
|
|
207
|
-
return selector(store);
|
|
151
|
+
if (!hasStateTreeNode(store)) {
|
|
152
|
+
return store;
|
|
208
153
|
}
|
|
209
|
-
|
|
154
|
+
const node = getStateTreeNode(store);
|
|
155
|
+
useAtomValue(node.snapshotAtom, { store: getGlobalStore() });
|
|
156
|
+
return selector ? selector(store) : store;
|
|
210
157
|
}
|
|
211
158
|
function createStoreContext() {
|
|
212
159
|
const Context = React.createContext(null);
|
|
@@ -227,20 +174,12 @@ function createStoreContext() {
|
|
|
227
174
|
}
|
|
228
175
|
function useTypedStoreSnapshot(selector) {
|
|
229
176
|
const store = useTypedStore();
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (hasStateTreeNode(store)) {
|
|
233
|
-
return onSnapshot(store, () => {
|
|
234
|
-
forceUpdate({});
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
return () => {
|
|
238
|
-
};
|
|
239
|
-
}, [store]);
|
|
240
|
-
if (selector) {
|
|
241
|
-
return selector(store);
|
|
177
|
+
if (!hasStateTreeNode(store)) {
|
|
178
|
+
return store;
|
|
242
179
|
}
|
|
243
|
-
|
|
180
|
+
const node = getStateTreeNode(store);
|
|
181
|
+
useAtomValue(node.snapshotAtom, { store: getGlobalStore() });
|
|
182
|
+
return selector ? selector(store) : store;
|
|
244
183
|
}
|
|
245
184
|
function useTypedIsAlive() {
|
|
246
185
|
const store = useTypedStore();
|
|
@@ -255,47 +194,33 @@ function createStoreContext() {
|
|
|
255
194
|
};
|
|
256
195
|
}
|
|
257
196
|
function useSnapshot(target) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
return disposer;
|
|
264
|
-
}, [target]);
|
|
265
|
-
return snapshot;
|
|
197
|
+
if (!hasStateTreeNode(target)) {
|
|
198
|
+
return target;
|
|
199
|
+
}
|
|
200
|
+
const node = getStateTreeNode(target);
|
|
201
|
+
return useAtomValue(node.snapshotAtom, { store: getGlobalStore() });
|
|
266
202
|
}
|
|
267
203
|
function useWatchPath(target, path, defaultValue) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
} else {
|
|
276
|
-
return defaultValue;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
return current;
|
|
280
|
-
});
|
|
281
|
-
useEffect(() => {
|
|
282
|
-
const disposer = onSnapshot(target, (newSnapshot) => {
|
|
283
|
-
const snapshot = newSnapshot;
|
|
204
|
+
if (!hasStateTreeNode(target)) {
|
|
205
|
+
return defaultValue;
|
|
206
|
+
}
|
|
207
|
+
const node = getStateTreeNode(target);
|
|
208
|
+
const pathAtom = useMemo(() => {
|
|
209
|
+
return atom((get) => {
|
|
210
|
+
const snapshot = get(node.snapshotAtom);
|
|
284
211
|
const parts = path.split(".");
|
|
285
212
|
let current = snapshot;
|
|
286
213
|
for (const part of parts) {
|
|
287
214
|
if (current && typeof current === "object" && part in current) {
|
|
288
215
|
current = current[part];
|
|
289
216
|
} else {
|
|
290
|
-
|
|
291
|
-
return;
|
|
217
|
+
return defaultValue;
|
|
292
218
|
}
|
|
293
219
|
}
|
|
294
|
-
|
|
220
|
+
return current;
|
|
295
221
|
});
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
return value;
|
|
222
|
+
}, [node, path, defaultValue]);
|
|
223
|
+
return useAtomValue(pathAtom, { store: getGlobalStore() });
|
|
299
224
|
}
|
|
300
225
|
function usePatches(target, callback) {
|
|
301
226
|
useEffect(() => {
|
|
@@ -332,20 +257,9 @@ function scheduleUpdate(update) {
|
|
|
332
257
|
}
|
|
333
258
|
}
|
|
334
259
|
function useIsAlive(target) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
});
|
|
339
|
-
useEffect(() => {
|
|
340
|
-
if (!hasStateTreeNode(target)) return;
|
|
341
|
-
const node = getStateTreeNode(target);
|
|
342
|
-
setIsAlive(node.$isAlive);
|
|
343
|
-
const disposer = onLifecycleChange(node, (alive) => {
|
|
344
|
-
setIsAlive(alive);
|
|
345
|
-
});
|
|
346
|
-
return disposer;
|
|
347
|
-
}, [target]);
|
|
348
|
-
return isAlive;
|
|
260
|
+
if (!hasStateTreeNode(target)) return false;
|
|
261
|
+
const node = getStateTreeNode(target);
|
|
262
|
+
return useAtomValue(node.isAliveAtom, { store: getGlobalStore() });
|
|
349
263
|
}
|
|
350
264
|
function useCleanup(cleanupFn) {
|
|
351
265
|
const cleanupRef = useRef(cleanupFn);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as jotai_vanilla_internals from 'jotai/vanilla/internals';
|
|
2
|
-
import { WritableAtom, createStore } from 'jotai';
|
|
2
|
+
import { WritableAtom, Atom, createStore } from 'jotai';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Core types for jotai-state-tree
|
|
@@ -332,6 +332,10 @@ declare class StateTreeNode implements IStateTreeNode {
|
|
|
332
332
|
private children;
|
|
333
333
|
/** Atom storing the raw value/snapshot */
|
|
334
334
|
valueAtom: WritableAtom<unknown, [unknown], void>;
|
|
335
|
+
/** Derived atom representing the snapshot of the entire node subtree */
|
|
336
|
+
snapshotAtom: Atom<unknown>;
|
|
337
|
+
/** Atom tracking the isAlive status */
|
|
338
|
+
isAliveAtom: WritableAtom<boolean, [boolean], void>;
|
|
335
339
|
/** Snapshot listeners */
|
|
336
340
|
private snapshotListeners;
|
|
337
341
|
/** Patch listeners */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as jotai_vanilla_internals from 'jotai/vanilla/internals';
|
|
2
|
-
import { WritableAtom, createStore } from 'jotai';
|
|
2
|
+
import { WritableAtom, Atom, createStore } from 'jotai';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Core types for jotai-state-tree
|
|
@@ -332,6 +332,10 @@ declare class StateTreeNode implements IStateTreeNode {
|
|
|
332
332
|
private children;
|
|
333
333
|
/** Atom storing the raw value/snapshot */
|
|
334
334
|
valueAtom: WritableAtom<unknown, [unknown], void>;
|
|
335
|
+
/** Derived atom representing the snapshot of the entire node subtree */
|
|
336
|
+
snapshotAtom: Atom<unknown>;
|
|
337
|
+
/** Atom tracking the isAlive status */
|
|
338
|
+
isAliveAtom: WritableAtom<boolean, [boolean], void>;
|
|
335
339
|
/** Snapshot listeners */
|
|
336
340
|
private snapshotListeners;
|
|
337
341
|
/** Patch listeners */
|
package/package.json
CHANGED
package/src/react.ts
CHANGED
|
@@ -18,6 +18,7 @@ import React, {
|
|
|
18
18
|
type FC,
|
|
19
19
|
} from "react";
|
|
20
20
|
import {
|
|
21
|
+
atom,
|
|
21
22
|
useAtom,
|
|
22
23
|
useAtomValue,
|
|
23
24
|
useSetAtom,
|
|
@@ -218,43 +219,12 @@ export function useLocalObservable<T>(
|
|
|
218
219
|
initializer: () => T,
|
|
219
220
|
dependencies: unknown[] = [],
|
|
220
221
|
): T {
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
// Initialize store
|
|
226
|
-
if (storeRef.current === null) {
|
|
227
|
-
storeRef.current = initializer();
|
|
228
|
-
|
|
229
|
-
// Subscribe to changes
|
|
230
|
-
if (hasStateTreeNode(storeRef.current)) {
|
|
231
|
-
disposerRef.current = onSnapshot(storeRef.current, () => {
|
|
232
|
-
forceUpdate({});
|
|
233
|
-
});
|
|
234
|
-
}
|
|
222
|
+
const store = useMemo(initializer, dependencies);
|
|
223
|
+
if (hasStateTreeNode(store)) {
|
|
224
|
+
const node = getStateTreeNode(store);
|
|
225
|
+
useAtomValue(node.snapshotAtom, { store: getGlobalStore() });
|
|
235
226
|
}
|
|
236
|
-
|
|
237
|
-
// Cleanup on unmount
|
|
238
|
-
useEffect(() => {
|
|
239
|
-
return () => {
|
|
240
|
-
disposerRef.current?.();
|
|
241
|
-
};
|
|
242
|
-
}, []);
|
|
243
|
-
|
|
244
|
-
// Reinitialize if dependencies change
|
|
245
|
-
useEffect(() => {
|
|
246
|
-
if (dependencies.length > 0) {
|
|
247
|
-
disposerRef.current?.();
|
|
248
|
-
storeRef.current = initializer();
|
|
249
|
-
if (hasStateTreeNode(storeRef.current)) {
|
|
250
|
-
disposerRef.current = onSnapshot(storeRef.current, () => {
|
|
251
|
-
forceUpdate({});
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}, dependencies);
|
|
256
|
-
|
|
257
|
-
return storeRef.current;
|
|
227
|
+
return store;
|
|
258
228
|
}
|
|
259
229
|
|
|
260
230
|
// ============================================================================
|
|
@@ -266,36 +236,11 @@ export function useLocalObservable<T>(
|
|
|
266
236
|
* This provides better concurrent mode support.
|
|
267
237
|
*/
|
|
268
238
|
export function useSyncedStore<T>(store: T): T {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
if (!hasStateTreeNode(store)) {
|
|
275
|
-
return () => {};
|
|
276
|
-
}
|
|
277
|
-
return onSnapshot(store, () => {
|
|
278
|
-
// Update cached snapshot on change
|
|
279
|
-
snapshotRef.current = getSnapshot(store);
|
|
280
|
-
callback();
|
|
281
|
-
});
|
|
282
|
-
},
|
|
283
|
-
[store],
|
|
284
|
-
);
|
|
285
|
-
|
|
286
|
-
const getSnapshotValue = useCallback(() => {
|
|
287
|
-
if (!hasStateTreeNode(store)) {
|
|
288
|
-
return null;
|
|
289
|
-
}
|
|
290
|
-
// Return cached snapshot for stable reference comparison
|
|
291
|
-
if (snapshotRef.current === null) {
|
|
292
|
-
snapshotRef.current = getSnapshot(store);
|
|
293
|
-
}
|
|
294
|
-
return snapshotRef.current;
|
|
295
|
-
}, [store]);
|
|
296
|
-
|
|
297
|
-
useSyncExternalStore(subscribe, getSnapshotValue, getSnapshotValue);
|
|
298
|
-
|
|
239
|
+
if (!hasStateTreeNode(store)) {
|
|
240
|
+
return store;
|
|
241
|
+
}
|
|
242
|
+
const node = getStateTreeNode(store);
|
|
243
|
+
useAtomValue(node.snapshotAtom, { store: getGlobalStore() });
|
|
299
244
|
return store;
|
|
300
245
|
}
|
|
301
246
|
|
|
@@ -350,22 +295,12 @@ export function useStoreSnapshot<T>(): T;
|
|
|
350
295
|
export function useStoreSnapshot<T, S>(selector: (store: T) => S): S;
|
|
351
296
|
export function useStoreSnapshot<T, S>(selector?: (store: T) => S): T | S {
|
|
352
297
|
const store = useStore<T>();
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
useEffect(() => {
|
|
356
|
-
if (hasStateTreeNode(store)) {
|
|
357
|
-
return onSnapshot(store, () => {
|
|
358
|
-
forceUpdate({});
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
return () => {};
|
|
362
|
-
}, [store]);
|
|
363
|
-
|
|
364
|
-
if (selector) {
|
|
365
|
-
return selector(store);
|
|
298
|
+
if (!hasStateTreeNode(store)) {
|
|
299
|
+
return store;
|
|
366
300
|
}
|
|
367
|
-
|
|
368
|
-
|
|
301
|
+
const node = getStateTreeNode(store);
|
|
302
|
+
useAtomValue(node.snapshotAtom, { store: getGlobalStore() });
|
|
303
|
+
return selector ? selector(store) : store;
|
|
369
304
|
}
|
|
370
305
|
|
|
371
306
|
// ============================================================================
|
|
@@ -424,22 +359,12 @@ export function createStoreContext<T>() {
|
|
|
424
359
|
function useTypedStoreSnapshot<S>(selector: (store: T) => S): S;
|
|
425
360
|
function useTypedStoreSnapshot<S>(selector?: (store: T) => S): T | S {
|
|
426
361
|
const store = useTypedStore();
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
useEffect(() => {
|
|
430
|
-
if (hasStateTreeNode(store)) {
|
|
431
|
-
return onSnapshot(store, () => {
|
|
432
|
-
forceUpdate({});
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
return () => {};
|
|
436
|
-
}, [store]);
|
|
437
|
-
|
|
438
|
-
if (selector) {
|
|
439
|
-
return selector(store);
|
|
362
|
+
if (!hasStateTreeNode(store)) {
|
|
363
|
+
return store;
|
|
440
364
|
}
|
|
441
|
-
|
|
442
|
-
|
|
365
|
+
const node = getStateTreeNode(store);
|
|
366
|
+
useAtomValue(node.snapshotAtom, { store: getGlobalStore() });
|
|
367
|
+
return selector ? selector(store) : store;
|
|
443
368
|
}
|
|
444
369
|
|
|
445
370
|
/**
|
|
@@ -463,20 +388,12 @@ export function createStoreContext<T>() {
|
|
|
463
388
|
// Snapshot Hooks
|
|
464
389
|
// ============================================================================
|
|
465
390
|
|
|
466
|
-
/**
|
|
467
|
-
* Hook that returns the current snapshot and re-renders on changes.
|
|
468
|
-
*/
|
|
469
391
|
export function useSnapshot<T>(target: unknown): T {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
});
|
|
476
|
-
return disposer;
|
|
477
|
-
}, [target]);
|
|
478
|
-
|
|
479
|
-
return snapshot;
|
|
392
|
+
if (!hasStateTreeNode(target)) {
|
|
393
|
+
return target as T;
|
|
394
|
+
}
|
|
395
|
+
const node = getStateTreeNode(target);
|
|
396
|
+
return useAtomValue(node.snapshotAtom, { store: getGlobalStore() }) as T;
|
|
480
397
|
}
|
|
481
398
|
|
|
482
399
|
/**
|
|
@@ -487,39 +404,27 @@ export function useWatchPath<T>(
|
|
|
487
404
|
path: string,
|
|
488
405
|
defaultValue?: T,
|
|
489
406
|
): T {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
} else {
|
|
498
|
-
return defaultValue as T;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
return current as T;
|
|
502
|
-
});
|
|
503
|
-
|
|
504
|
-
useEffect(() => {
|
|
505
|
-
const disposer = onSnapshot(target, (newSnapshot) => {
|
|
506
|
-
const snapshot = newSnapshot as Record<string, unknown>;
|
|
407
|
+
if (!hasStateTreeNode(target)) {
|
|
408
|
+
return defaultValue as T;
|
|
409
|
+
}
|
|
410
|
+
const node = getStateTreeNode(target);
|
|
411
|
+
const pathAtom = useMemo(() => {
|
|
412
|
+
return atom((get) => {
|
|
413
|
+
const snapshot = get(node.snapshotAtom) as Record<string, unknown>;
|
|
507
414
|
const parts = path.split(".");
|
|
508
415
|
let current: unknown = snapshot;
|
|
509
416
|
for (const part of parts) {
|
|
510
417
|
if (current && typeof current === "object" && part in current) {
|
|
511
418
|
current = (current as Record<string, unknown>)[part];
|
|
512
419
|
} else {
|
|
513
|
-
|
|
514
|
-
return;
|
|
420
|
+
return defaultValue as T;
|
|
515
421
|
}
|
|
516
422
|
}
|
|
517
|
-
|
|
423
|
+
return current as T;
|
|
518
424
|
});
|
|
519
|
-
|
|
520
|
-
}, [target, path, defaultValue]);
|
|
425
|
+
}, [node, path, defaultValue]);
|
|
521
426
|
|
|
522
|
-
return
|
|
427
|
+
return useAtomValue(pathAtom, { store: getGlobalStore() });
|
|
523
428
|
}
|
|
524
429
|
|
|
525
430
|
/**
|
|
@@ -602,27 +507,9 @@ export function scheduleUpdate(update: () => void): void {
|
|
|
602
507
|
* Uses proper subscription instead of polling for better performance.
|
|
603
508
|
*/
|
|
604
509
|
export function useIsAlive(target: unknown): boolean {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
useEffect(() => {
|
|
611
|
-
if (!hasStateTreeNode(target)) return;
|
|
612
|
-
|
|
613
|
-
const node = getStateTreeNode(target);
|
|
614
|
-
setIsAlive(node.$isAlive);
|
|
615
|
-
|
|
616
|
-
// Subscribe to lifecycle changes using proper event system
|
|
617
|
-
// This is much more efficient than polling
|
|
618
|
-
const disposer = onLifecycleChange(node, (alive) => {
|
|
619
|
-
setIsAlive(alive);
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
return disposer;
|
|
623
|
-
}, [target]);
|
|
624
|
-
|
|
625
|
-
return isAlive;
|
|
510
|
+
if (!hasStateTreeNode(target)) return false;
|
|
511
|
+
const node = getStateTreeNode(target);
|
|
512
|
+
return useAtomValue(node.isAliveAtom, { store: getGlobalStore() });
|
|
626
513
|
}
|
|
627
514
|
|
|
628
515
|
/**
|
package/src/tree.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - Properly cleans up all registries on node destruction
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { atom, createStore, type WritableAtom } from "jotai";
|
|
12
|
+
import { atom, createStore, type WritableAtom, type Atom } from "jotai";
|
|
13
13
|
import type {
|
|
14
14
|
IStateTreeNode,
|
|
15
15
|
IType,
|
|
@@ -226,6 +226,12 @@ export class StateTreeNode implements IStateTreeNode {
|
|
|
226
226
|
/** Atom storing the raw value/snapshot */
|
|
227
227
|
valueAtom: WritableAtom<unknown, [unknown], void>;
|
|
228
228
|
|
|
229
|
+
/** Derived atom representing the snapshot of the entire node subtree */
|
|
230
|
+
snapshotAtom: Atom<unknown>;
|
|
231
|
+
|
|
232
|
+
/** Atom tracking the isAlive status */
|
|
233
|
+
isAliveAtom: WritableAtom<boolean, [boolean], void>;
|
|
234
|
+
|
|
229
235
|
/** Snapshot listeners */
|
|
230
236
|
private snapshotListeners = new Set<(snapshot: unknown) => void>();
|
|
231
237
|
|
|
@@ -280,6 +286,47 @@ export class StateTreeNode implements IStateTreeNode {
|
|
|
280
286
|
// Create the value atom
|
|
281
287
|
this.valueAtom = atom(initialValue);
|
|
282
288
|
|
|
289
|
+
this.isAliveAtom = atom(true);
|
|
290
|
+
|
|
291
|
+
this.snapshotAtom = atom((get) => {
|
|
292
|
+
const value = get(this.valueAtom);
|
|
293
|
+
|
|
294
|
+
if (this.$type._kind === "model") {
|
|
295
|
+
const res: Record<string, unknown> = {};
|
|
296
|
+
for (const [key, childNode] of this.children.entries()) {
|
|
297
|
+
res[key] = get(childNode.snapshotAtom);
|
|
298
|
+
}
|
|
299
|
+
if (value && typeof value === "object") {
|
|
300
|
+
for (const key of Object.keys(value)) {
|
|
301
|
+
if (!this.children.has(key)) {
|
|
302
|
+
res[key] = (value as Record<string, unknown>)[key];
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return this.postProcessor ? this.postProcessor(res) : res;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (this.$type._kind === "array") {
|
|
310
|
+
const res: unknown[] = [];
|
|
311
|
+
const childrenKeys = Array.from(this.children.keys()).sort((a, b) => Number(a) - Number(b));
|
|
312
|
+
for (const key of childrenKeys) {
|
|
313
|
+
const childNode = this.children.get(key)!;
|
|
314
|
+
res.push(get(childNode.snapshotAtom));
|
|
315
|
+
}
|
|
316
|
+
return res;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (this.$type._kind === "map") {
|
|
320
|
+
const res: Record<string, unknown> = {};
|
|
321
|
+
for (const [key, childNode] of this.children.entries()) {
|
|
322
|
+
res[key] = get(childNode.snapshotAtom);
|
|
323
|
+
}
|
|
324
|
+
return res;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return value;
|
|
328
|
+
});
|
|
329
|
+
|
|
283
330
|
// Register this node with WeakRef
|
|
284
331
|
nodeRegistry.set(this.$id, { node: new WeakRef(this), instance: null });
|
|
285
332
|
|
|
@@ -515,6 +562,11 @@ export class StateTreeNode implements IStateTreeNode {
|
|
|
515
562
|
|
|
516
563
|
// Mark as dead
|
|
517
564
|
this.$isAlive = false;
|
|
565
|
+
try {
|
|
566
|
+
globalStore.set(this.isAliveAtom, false);
|
|
567
|
+
} catch (e) {
|
|
568
|
+
// Ignore store errors during teardown
|
|
569
|
+
}
|
|
518
570
|
|
|
519
571
|
// Notify lifecycle listeners
|
|
520
572
|
notifyLifecycleChange(this, false);
|
|
@@ -800,6 +852,11 @@ export function clearAllRegistries(): void {
|
|
|
800
852
|
const node = entry.node.deref();
|
|
801
853
|
if (node) {
|
|
802
854
|
node.$isAlive = false;
|
|
855
|
+
try {
|
|
856
|
+
globalStore.set(node.isAliveAtom, false);
|
|
857
|
+
} catch (e) {
|
|
858
|
+
// Ignore store errors during teardown
|
|
859
|
+
}
|
|
803
860
|
}
|
|
804
861
|
}
|
|
805
862
|
nodeRegistry.clear();
|