r-state-tree 0.8.0 → 0.8.2

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/README.md CHANGED
@@ -139,7 +139,7 @@ app.todo.title;
139
139
 
140
140
  - Always create with `createStore()` and attach with `mount()`. Stores cannot be constructed with `new` directly.
141
141
  - Prefer no custom constructor. Type stores as `Store<Props>` and access props via `this.props`.
142
- - Constructors may register framework-owned reactions and effects after `super(props)`. Gate external resource acquisition on `isMounted` and return cleanup from effects.
142
+ - Constructors may register framework-owned reactions and effects after `super(props)`. Store registrations activate when mounting and are disposed with the store; return cleanup from effects to release resources.
143
143
  - Do not shadow or re-declare `props` as a class field; `props` is read-only. Use the generic `Store<{ ... }>` for typing.
144
144
 
145
145
  ```ts
@@ -205,14 +205,13 @@ class ItemsStore extends Store {
205
205
 
206
206
  ### Store lifetime and owned effects
207
207
 
208
- Mounted stores expose reactive `isMounted` state and implement `Disposable`:
208
+ Stores implement `Disposable`, and their owned effects run only during the mounted lifetime:
209
209
 
210
210
  ```ts
211
211
  class TodoStore extends Store {
212
212
  constructor(props) {
213
213
  super(props);
214
214
  this.effect(() => {
215
- if (!this.isMounted) return;
216
215
  const connection = connect();
217
216
  return () => connection.close();
218
217
  });
@@ -243,6 +242,10 @@ class TodoStore extends Store {
243
242
  }
244
243
  ```
245
244
 
245
+ `Store.effect()` and `Store.reaction()` register mount-scoped behavior. Mounting first links the complete store tree and commits its private mounted state bottom-up in one transaction. Registrations then activate bottom-up, so even a grandchild's initial effect sees a complete tree. A reaction establishes its initial value at activation and still skips its initial callback. Calling the disposer returned during construction cancels the pending registration; after mount, it disposes the live subscription. All registrations are disposed automatically with the store.
246
+
247
+ Registration activation is non-reentrant. If an active effect or reaction lazily materializes another child store, the new child's registrations are queued until the current callback and child getter stack have returned. This prevents initialization behavior from running inside the getter that created the store.
248
+
246
249
  ### Context
247
250
 
248
251
  Share data across the store tree without prop drilling:
@@ -875,23 +878,19 @@ This keeps snapshot hydration predictable: `create(snapshot)` and `applySnapshot
875
878
 
876
879
  ### Model attachment and disposal
877
880
 
878
- The public `parent` relationship is reactive. Register a model-owned reaction in the constructor to observe attachment and detachment:
881
+ Models are inert state trees: creating or attaching one does not start reactions or effects. The public `parent` relationship remains reactive, so an external owner may observe it explicitly when needed:
879
882
 
880
883
  ```ts
881
- class TodoModel extends Model {
882
- @child tags: TagModel[] = [];
883
-
884
- constructor() {
885
- super();
886
- this.reaction(() => this.parent, (parent, previousParent) => {
887
- if (previousParent) console.log("detached", previousParent);
888
- if (parent) console.log("attached", parent);
889
- });
884
+ const stop = reaction(
885
+ () => todo.parent,
886
+ (parent, previousParent) => {
887
+ if (previousParent) console.log("detached", previousParent);
888
+ if (parent) console.log("attached", parent);
890
889
  }
891
- }
890
+ );
892
891
  ```
893
892
 
894
- Detachment is reversible; model-owned effects survive reattachment. `model[Symbol.dispose]()` is terminal and recursively disposes owned child models, but never model refs.
893
+ The external caller owns `stop`. Detachment is reversible. `model[Symbol.dispose]()` is terminal and recursively disposes owned child models, but never model refs.
895
894
 
896
895
  ### Model configuration
897
896
 
@@ -1204,20 +1203,15 @@ class ListModel extends Model {
1204
1203
  }
1205
1204
  ```
1206
1205
 
1207
- Lifecycle registration:
1206
+ Store lifecycle registration:
1208
1207
 
1209
1208
  ```ts
1210
- class M extends Model {
1211
- constructor() {
1212
- super();
1213
- this.reaction(() => this.parent, (parent, previousParent) => {});
1214
- }
1215
- }
1216
1209
  class S extends Store {
1217
1210
  constructor(props) {
1218
1211
  super(props);
1219
1212
  this.effect(() => {
1220
- if (!this.isMounted) return;
1213
+ const resource = acquireResource();
1214
+ return () => resource.dispose();
1221
1215
  });
1222
1216
  }
1223
1217
  }
@@ -1280,8 +1274,8 @@ When child stores are created during mount with `models` that point back into th
1280
1274
  - Decorators (Stores): `@child`, `@model`
1281
1275
  - Core: `createStore`, `mount`, `Symbol.dispose`, `updateStore`
1282
1276
  - Snapshots: `onSnapshot`, `toSnapshot`, `applySnapshot`, `onSnapshotDiff`
1283
- - Lifecycle: reactive `Store.isMounted`, reactive `Model.parent`, and owned `reaction`/`effect`
1284
- - Best practices: domain in Models; delegate from Stores; stable keys for `@child`; gate resources on lifecycle state; don’t shadow `props`.
1277
+ - Lifecycle: Store-owned `reaction`/`effect`, reactive `Model.parent`, and `Symbol.dispose`
1278
+ - Best practices: domain in Models; delegate from Stores; stable keys for `@child`; return cleanup from store effects; don’t shadow `props`.
1285
1279
 
1286
1280
  ## Testing
1287
1281
 
@@ -7,7 +7,5 @@ export default class Model implements Disposable {
7
7
  }, snapshot?: Snapshot<T>): T;
8
8
  constructor();
9
9
  get parent(): Model | null;
10
- reaction<T>(track: () => T, callback: (value: T, previousValue: T) => void): () => void;
11
- effect(callback: () => void | (() => void)): () => void;
12
10
  [Symbol.dispose](): void;
13
11
  }
@@ -20,16 +20,12 @@ export declare class ModelAdministration extends PreactObjectAdministration<any>
20
20
  private computedSnapshot;
21
21
  private snapshotMap;
22
22
  private contextCache;
23
- private ownedDisposers;
24
23
  private disposed;
25
24
  parentName: PropertyKey | null;
26
25
  get parent(): ModelAdministration | null;
27
26
  set parent(value: ModelAdministration | null);
28
27
  setConfiguration(configurationGetter: () => ModelConfiguration<any>): void;
29
28
  assertUsable(): void;
30
- private own;
31
- reaction<T>(track: () => T, callback: (value: T, previousValue: T) => void): () => void;
32
- effect(callback: () => void | (() => void)): () => void;
33
29
  private get configuration();
34
30
  private getReferencedAtom;
35
31
  private setState;
@@ -1993,6 +1993,32 @@ function validateStoreChildValue(value, propertyName) {
1993
1993
  );
1994
1994
  }
1995
1995
  class StoreAdministration extends PreactObjectAdministration {
1996
+ static pendingActivations = [];
1997
+ static isFlushingActivations = false;
1998
+ static currentActivationDepth = 0;
1999
+ static flushReactiveRegistrations(stores) {
2000
+ const depth = this.isFlushingActivations ? this.currentActivationDepth + 1 : 0;
2001
+ this.pendingActivations.push(...stores.map((store) => ({ store, depth })));
2002
+ if (this.isFlushingActivations) return;
2003
+ this.isFlushingActivations = true;
2004
+ try {
2005
+ let entry;
2006
+ while (entry = this.pendingActivations.shift()) {
2007
+ this.currentActivationDepth = entry.depth;
2008
+ if (entry.depth > MAX_MOUNT_DEPTH) {
2009
+ throw createCircularMountError({
2010
+ storeName: entry.store.proxy.constructor.name || "Store",
2011
+ modelsKeys: Object.keys(entry.store.proxy.props?.models ?? {})
2012
+ });
2013
+ }
2014
+ entry.store.startReactiveRegistrations();
2015
+ }
2016
+ } finally {
2017
+ this.pendingActivations.length = 0;
2018
+ this.currentActivationDepth = 0;
2019
+ this.isFlushingActivations = false;
2020
+ }
2021
+ }
1996
2022
  static proxyTraps = Object.assign(
1997
2023
  {},
1998
2024
  PreactObjectAdministration.proxyTraps,
@@ -2032,11 +2058,11 @@ class StoreAdministration extends PreactObjectAdministration {
2032
2058
  }
2033
2059
  );
2034
2060
  parent = null;
2035
- mountedSignal = createSignal(false);
2061
+ mounted = false;
2036
2062
  disposed = false;
2037
2063
  contextCache = /* @__PURE__ */ new Map();
2038
2064
  childStoreDataMap = /* @__PURE__ */ new Map();
2039
- ownedDisposers = /* @__PURE__ */ new Set();
2065
+ reactiveRegistrations = /* @__PURE__ */ new Set();
2040
2066
  configurationGetter;
2041
2067
  setConfiguration(configurationGetter) {
2042
2068
  this.configurationGetter = configurationGetter;
@@ -2060,7 +2086,9 @@ class StoreAdministration extends PreactObjectAdministration {
2060
2086
  }
2061
2087
  });
2062
2088
  childStoreData.value.set(stores);
2063
- stores.forEach((s) => getStoreAdm(s).mount(this, name));
2089
+ if (this.isMounted) {
2090
+ stores.forEach((s) => getStoreAdm(s).mount(this, name));
2091
+ }
2064
2092
  return stores;
2065
2093
  }
2066
2094
  const newStores = /* @__PURE__ */ new Set();
@@ -2111,7 +2139,9 @@ class StoreAdministration extends PreactObjectAdministration {
2111
2139
  signalsCore.batch(() => childStoreData.value.set(stores));
2112
2140
  }
2113
2141
  removedStores.forEach((s) => getStoreAdm(s).dispose(true));
2114
- newStores.forEach((s) => getStoreAdm(s).mount(this, name));
2142
+ if (this.isMounted) {
2143
+ newStores.forEach((s) => getStoreAdm(s).mount(this, name));
2144
+ }
2115
2145
  return stores;
2116
2146
  }
2117
2147
  setSingleStore(name, element) {
@@ -2130,7 +2160,9 @@ class StoreAdministration extends PreactObjectAdministration {
2130
2160
  }
2131
2161
  const childStore = this.createChildStore(element);
2132
2162
  signalsCore.batch(() => childStoreData.value.set(childStore));
2133
- getStoreAdm(childStore).mount(this, name);
2163
+ if (this.isMounted) {
2164
+ getStoreAdm(childStore).mount(this, name);
2165
+ }
2134
2166
  return childStore;
2135
2167
  } else {
2136
2168
  signalsCore.batch(() => updateProps(oldStore.props, props));
@@ -2188,7 +2220,7 @@ class StoreAdministration extends PreactObjectAdministration {
2188
2220
  return !this.parent;
2189
2221
  }
2190
2222
  get isMounted() {
2191
- return this.mountedSignal.get();
2223
+ return this.mounted;
2192
2224
  }
2193
2225
  getContextValue(contextId, provideSymbol, defaultValue, hasDefault) {
2194
2226
  let computed2 = this.contextCache.get(contextId);
@@ -2223,31 +2255,37 @@ class StoreAdministration extends PreactObjectAdministration {
2223
2255
  }
2224
2256
  return void 0;
2225
2257
  }
2226
- own(disposer) {
2258
+ register(start) {
2227
2259
  if (this.disposed) {
2228
- disposer();
2229
2260
  throw new Error(
2230
2261
  "r-state-tree: cannot register reactive resources on a disposed store"
2231
2262
  );
2232
2263
  }
2233
- let active = true;
2234
- const wrapped = () => {
2235
- if (!active) return;
2236
- active = false;
2237
- this.ownedDisposers.delete(wrapped);
2238
- disposer();
2264
+ const registration = { start, disposed: false };
2265
+ this.reactiveRegistrations.add(registration);
2266
+ if (this.isMounted) registration.stop = registration.start();
2267
+ return () => {
2268
+ if (registration.disposed) return;
2269
+ registration.disposed = true;
2270
+ this.reactiveRegistrations.delete(registration);
2271
+ registration.stop?.();
2272
+ registration.stop = void 0;
2239
2273
  };
2240
- this.ownedDisposers.add(wrapped);
2241
- return wrapped;
2274
+ }
2275
+ startReactiveRegistrations() {
2276
+ this.reactiveRegistrations.forEach((registration) => {
2277
+ if (!registration.disposed && !registration.stop) {
2278
+ registration.stop = registration.start();
2279
+ }
2280
+ });
2242
2281
  }
2243
2282
  reaction(track, callback) {
2244
- const unsub = reaction(track, callback);
2245
- return this.own(unsub);
2283
+ return this.register(() => reaction(track, callback));
2246
2284
  }
2247
2285
  effect(callback) {
2248
- return this.own(signalsCore.effect(callback));
2286
+ return this.register(() => signalsCore.effect(callback));
2249
2287
  }
2250
- mount(parent = null, childName) {
2288
+ mount(parent = null, childName, activationQueue) {
2251
2289
  if (this.disposed) {
2252
2290
  throw new Error("r-state-tree: cannot mount a disposed store");
2253
2291
  }
@@ -2263,19 +2301,27 @@ class StoreAdministration extends PreactObjectAdministration {
2263
2301
  throw createCircularMountError(frame);
2264
2302
  }
2265
2303
  mountingStack.push(frame);
2304
+ const isOutermostMount = activationQueue === void 0;
2305
+ const registrationsToStart = activationQueue ?? [];
2266
2306
  try {
2267
2307
  signalsCore.batch(() => {
2268
2308
  this.parent = parent || null;
2269
2309
  this.childStoreDataMap.forEach(({ value }, name) => {
2270
2310
  const stores = value.get();
2271
2311
  if (Array.isArray(stores)) {
2272
- stores?.forEach((s) => getStoreAdm(s)?.mount(this, name));
2312
+ stores?.forEach(
2313
+ (s) => getStoreAdm(s)?.mount(this, name, registrationsToStart)
2314
+ );
2273
2315
  } else if (stores) {
2274
- getStoreAdm(stores)?.mount(this, name);
2316
+ getStoreAdm(stores)?.mount(this, name, registrationsToStart);
2275
2317
  }
2276
2318
  });
2277
- this.mountedSignal.set(true);
2319
+ this.mounted = true;
2320
+ registrationsToStart.push(this);
2278
2321
  });
2322
+ if (isOutermostMount) {
2323
+ StoreAdministration.flushReactiveRegistrations(registrationsToStart);
2324
+ }
2279
2325
  } catch (error) {
2280
2326
  if (error instanceof Error && (error instanceof RangeError && /call stack/i.test(error.message) || /cycle detected/i.test(error.message))) {
2281
2327
  throw createCircularMountError(frame);
@@ -2307,9 +2353,14 @@ class StoreAdministration extends PreactObjectAdministration {
2307
2353
  this.contextCache.clear();
2308
2354
  this.parent = null;
2309
2355
  });
2310
- this.mountedSignal.set(false);
2311
2356
  this.disposed = true;
2312
- Array.from(this.ownedDisposers).forEach((dispose) => dispose());
2357
+ this.reactiveRegistrations.forEach((registration) => {
2358
+ registration.disposed = true;
2359
+ registration.stop?.();
2360
+ registration.stop = void 0;
2361
+ });
2362
+ this.reactiveRegistrations.clear();
2363
+ this.mounted = false;
2313
2364
  }
2314
2365
  }
2315
2366
  let initEnabled$1 = false;
@@ -2355,9 +2406,6 @@ class Store {
2355
2406
  get key() {
2356
2407
  return this.props.key;
2357
2408
  }
2358
- get isMounted() {
2359
- return getStoreAdm(this).isMounted;
2360
- }
2361
2409
  reaction(track, callback) {
2362
2410
  return getStoreAdm(this).reaction(track, callback);
2363
2411
  }
@@ -2711,7 +2759,6 @@ class ModelAdministration extends PreactObjectAdministration {
2711
2759
  computedSnapshot;
2712
2760
  snapshotMap = /* @__PURE__ */ new Map();
2713
2761
  contextCache = /* @__PURE__ */ new Map();
2714
- ownedDisposers = /* @__PURE__ */ new Set();
2715
2762
  disposed = false;
2716
2763
  parentName = null;
2717
2764
  get parent() {
@@ -2732,24 +2779,6 @@ class ModelAdministration extends PreactObjectAdministration {
2732
2779
  throw new Error("r-state-tree: cannot use a disposed model");
2733
2780
  }
2734
2781
  }
2735
- own(disposer) {
2736
- this.assertUsable();
2737
- let active = true;
2738
- const wrapped = () => {
2739
- if (!active) return;
2740
- active = false;
2741
- this.ownedDisposers.delete(wrapped);
2742
- disposer();
2743
- };
2744
- this.ownedDisposers.add(wrapped);
2745
- return wrapped;
2746
- }
2747
- reaction(track, callback) {
2748
- return this.own(reaction(track, callback));
2749
- }
2750
- effect(callback) {
2751
- return this.own(signalsCore.effect(callback));
2752
- }
2753
2782
  get configuration() {
2754
2783
  return this.configurationGetter?.() ?? {};
2755
2784
  }
@@ -3041,7 +3070,6 @@ class ModelAdministration extends PreactObjectAdministration {
3041
3070
  this.modelsTraceUnsub.clear();
3042
3071
  this.contextCache.forEach((computed2) => computed2.clear());
3043
3072
  this.contextCache.clear();
3044
- Array.from(this.ownedDisposers).forEach((dispose) => dispose());
3045
3073
  }
3046
3074
  getContextValue(contextId, provideSymbol, defaultValue, hasDefault) {
3047
3075
  let computed2 = this.contextCache.get(contextId);
@@ -3291,12 +3319,6 @@ class Model {
3291
3319
  get parent() {
3292
3320
  return getModelAdm(this).parent?.proxy ?? null;
3293
3321
  }
3294
- reaction(track, callback) {
3295
- return getModelAdm(this).reaction(track, callback);
3296
- }
3297
- effect(callback) {
3298
- return getModelAdm(this).effect(callback);
3299
- }
3300
3322
  [Symbol.dispose]() {
3301
3323
  getModelAdm(this).dispose();
3302
3324
  }
@@ -1992,6 +1992,32 @@ function validateStoreChildValue(value, propertyName) {
1992
1992
  );
1993
1993
  }
1994
1994
  class StoreAdministration extends PreactObjectAdministration {
1995
+ static pendingActivations = [];
1996
+ static isFlushingActivations = false;
1997
+ static currentActivationDepth = 0;
1998
+ static flushReactiveRegistrations(stores) {
1999
+ const depth = this.isFlushingActivations ? this.currentActivationDepth + 1 : 0;
2000
+ this.pendingActivations.push(...stores.map((store) => ({ store, depth })));
2001
+ if (this.isFlushingActivations) return;
2002
+ this.isFlushingActivations = true;
2003
+ try {
2004
+ let entry;
2005
+ while (entry = this.pendingActivations.shift()) {
2006
+ this.currentActivationDepth = entry.depth;
2007
+ if (entry.depth > MAX_MOUNT_DEPTH) {
2008
+ throw createCircularMountError({
2009
+ storeName: entry.store.proxy.constructor.name || "Store",
2010
+ modelsKeys: Object.keys(entry.store.proxy.props?.models ?? {})
2011
+ });
2012
+ }
2013
+ entry.store.startReactiveRegistrations();
2014
+ }
2015
+ } finally {
2016
+ this.pendingActivations.length = 0;
2017
+ this.currentActivationDepth = 0;
2018
+ this.isFlushingActivations = false;
2019
+ }
2020
+ }
1995
2021
  static proxyTraps = Object.assign(
1996
2022
  {},
1997
2023
  PreactObjectAdministration.proxyTraps,
@@ -2031,11 +2057,11 @@ class StoreAdministration extends PreactObjectAdministration {
2031
2057
  }
2032
2058
  );
2033
2059
  parent = null;
2034
- mountedSignal = createSignal(false);
2060
+ mounted = false;
2035
2061
  disposed = false;
2036
2062
  contextCache = /* @__PURE__ */ new Map();
2037
2063
  childStoreDataMap = /* @__PURE__ */ new Map();
2038
- ownedDisposers = /* @__PURE__ */ new Set();
2064
+ reactiveRegistrations = /* @__PURE__ */ new Set();
2039
2065
  configurationGetter;
2040
2066
  setConfiguration(configurationGetter) {
2041
2067
  this.configurationGetter = configurationGetter;
@@ -2059,7 +2085,9 @@ class StoreAdministration extends PreactObjectAdministration {
2059
2085
  }
2060
2086
  });
2061
2087
  childStoreData.value.set(stores);
2062
- stores.forEach((s) => getStoreAdm(s).mount(this, name));
2088
+ if (this.isMounted) {
2089
+ stores.forEach((s) => getStoreAdm(s).mount(this, name));
2090
+ }
2063
2091
  return stores;
2064
2092
  }
2065
2093
  const newStores = /* @__PURE__ */ new Set();
@@ -2110,7 +2138,9 @@ class StoreAdministration extends PreactObjectAdministration {
2110
2138
  batch(() => childStoreData.value.set(stores));
2111
2139
  }
2112
2140
  removedStores.forEach((s) => getStoreAdm(s).dispose(true));
2113
- newStores.forEach((s) => getStoreAdm(s).mount(this, name));
2141
+ if (this.isMounted) {
2142
+ newStores.forEach((s) => getStoreAdm(s).mount(this, name));
2143
+ }
2114
2144
  return stores;
2115
2145
  }
2116
2146
  setSingleStore(name, element) {
@@ -2129,7 +2159,9 @@ class StoreAdministration extends PreactObjectAdministration {
2129
2159
  }
2130
2160
  const childStore = this.createChildStore(element);
2131
2161
  batch(() => childStoreData.value.set(childStore));
2132
- getStoreAdm(childStore).mount(this, name);
2162
+ if (this.isMounted) {
2163
+ getStoreAdm(childStore).mount(this, name);
2164
+ }
2133
2165
  return childStore;
2134
2166
  } else {
2135
2167
  batch(() => updateProps(oldStore.props, props));
@@ -2187,7 +2219,7 @@ class StoreAdministration extends PreactObjectAdministration {
2187
2219
  return !this.parent;
2188
2220
  }
2189
2221
  get isMounted() {
2190
- return this.mountedSignal.get();
2222
+ return this.mounted;
2191
2223
  }
2192
2224
  getContextValue(contextId, provideSymbol, defaultValue, hasDefault) {
2193
2225
  let computed2 = this.contextCache.get(contextId);
@@ -2222,31 +2254,37 @@ class StoreAdministration extends PreactObjectAdministration {
2222
2254
  }
2223
2255
  return void 0;
2224
2256
  }
2225
- own(disposer) {
2257
+ register(start) {
2226
2258
  if (this.disposed) {
2227
- disposer();
2228
2259
  throw new Error(
2229
2260
  "r-state-tree: cannot register reactive resources on a disposed store"
2230
2261
  );
2231
2262
  }
2232
- let active = true;
2233
- const wrapped = () => {
2234
- if (!active) return;
2235
- active = false;
2236
- this.ownedDisposers.delete(wrapped);
2237
- disposer();
2263
+ const registration = { start, disposed: false };
2264
+ this.reactiveRegistrations.add(registration);
2265
+ if (this.isMounted) registration.stop = registration.start();
2266
+ return () => {
2267
+ if (registration.disposed) return;
2268
+ registration.disposed = true;
2269
+ this.reactiveRegistrations.delete(registration);
2270
+ registration.stop?.();
2271
+ registration.stop = void 0;
2238
2272
  };
2239
- this.ownedDisposers.add(wrapped);
2240
- return wrapped;
2273
+ }
2274
+ startReactiveRegistrations() {
2275
+ this.reactiveRegistrations.forEach((registration) => {
2276
+ if (!registration.disposed && !registration.stop) {
2277
+ registration.stop = registration.start();
2278
+ }
2279
+ });
2241
2280
  }
2242
2281
  reaction(track, callback) {
2243
- const unsub = reaction(track, callback);
2244
- return this.own(unsub);
2282
+ return this.register(() => reaction(track, callback));
2245
2283
  }
2246
2284
  effect(callback) {
2247
- return this.own(effect(callback));
2285
+ return this.register(() => effect(callback));
2248
2286
  }
2249
- mount(parent = null, childName) {
2287
+ mount(parent = null, childName, activationQueue) {
2250
2288
  if (this.disposed) {
2251
2289
  throw new Error("r-state-tree: cannot mount a disposed store");
2252
2290
  }
@@ -2262,19 +2300,27 @@ class StoreAdministration extends PreactObjectAdministration {
2262
2300
  throw createCircularMountError(frame);
2263
2301
  }
2264
2302
  mountingStack.push(frame);
2303
+ const isOutermostMount = activationQueue === void 0;
2304
+ const registrationsToStart = activationQueue ?? [];
2265
2305
  try {
2266
2306
  batch(() => {
2267
2307
  this.parent = parent || null;
2268
2308
  this.childStoreDataMap.forEach(({ value }, name) => {
2269
2309
  const stores = value.get();
2270
2310
  if (Array.isArray(stores)) {
2271
- stores?.forEach((s) => getStoreAdm(s)?.mount(this, name));
2311
+ stores?.forEach(
2312
+ (s) => getStoreAdm(s)?.mount(this, name, registrationsToStart)
2313
+ );
2272
2314
  } else if (stores) {
2273
- getStoreAdm(stores)?.mount(this, name);
2315
+ getStoreAdm(stores)?.mount(this, name, registrationsToStart);
2274
2316
  }
2275
2317
  });
2276
- this.mountedSignal.set(true);
2318
+ this.mounted = true;
2319
+ registrationsToStart.push(this);
2277
2320
  });
2321
+ if (isOutermostMount) {
2322
+ StoreAdministration.flushReactiveRegistrations(registrationsToStart);
2323
+ }
2278
2324
  } catch (error) {
2279
2325
  if (error instanceof Error && (error instanceof RangeError && /call stack/i.test(error.message) || /cycle detected/i.test(error.message))) {
2280
2326
  throw createCircularMountError(frame);
@@ -2306,9 +2352,14 @@ class StoreAdministration extends PreactObjectAdministration {
2306
2352
  this.contextCache.clear();
2307
2353
  this.parent = null;
2308
2354
  });
2309
- this.mountedSignal.set(false);
2310
2355
  this.disposed = true;
2311
- Array.from(this.ownedDisposers).forEach((dispose) => dispose());
2356
+ this.reactiveRegistrations.forEach((registration) => {
2357
+ registration.disposed = true;
2358
+ registration.stop?.();
2359
+ registration.stop = void 0;
2360
+ });
2361
+ this.reactiveRegistrations.clear();
2362
+ this.mounted = false;
2312
2363
  }
2313
2364
  }
2314
2365
  let initEnabled$1 = false;
@@ -2354,9 +2405,6 @@ class Store {
2354
2405
  get key() {
2355
2406
  return this.props.key;
2356
2407
  }
2357
- get isMounted() {
2358
- return getStoreAdm(this).isMounted;
2359
- }
2360
2408
  reaction(track, callback) {
2361
2409
  return getStoreAdm(this).reaction(track, callback);
2362
2410
  }
@@ -2710,7 +2758,6 @@ class ModelAdministration extends PreactObjectAdministration {
2710
2758
  computedSnapshot;
2711
2759
  snapshotMap = /* @__PURE__ */ new Map();
2712
2760
  contextCache = /* @__PURE__ */ new Map();
2713
- ownedDisposers = /* @__PURE__ */ new Set();
2714
2761
  disposed = false;
2715
2762
  parentName = null;
2716
2763
  get parent() {
@@ -2731,24 +2778,6 @@ class ModelAdministration extends PreactObjectAdministration {
2731
2778
  throw new Error("r-state-tree: cannot use a disposed model");
2732
2779
  }
2733
2780
  }
2734
- own(disposer) {
2735
- this.assertUsable();
2736
- let active = true;
2737
- const wrapped = () => {
2738
- if (!active) return;
2739
- active = false;
2740
- this.ownedDisposers.delete(wrapped);
2741
- disposer();
2742
- };
2743
- this.ownedDisposers.add(wrapped);
2744
- return wrapped;
2745
- }
2746
- reaction(track, callback) {
2747
- return this.own(reaction(track, callback));
2748
- }
2749
- effect(callback) {
2750
- return this.own(effect(callback));
2751
- }
2752
2781
  get configuration() {
2753
2782
  return this.configurationGetter?.() ?? {};
2754
2783
  }
@@ -3040,7 +3069,6 @@ class ModelAdministration extends PreactObjectAdministration {
3040
3069
  this.modelsTraceUnsub.clear();
3041
3070
  this.contextCache.forEach((computed2) => computed2.clear());
3042
3071
  this.contextCache.clear();
3043
- Array.from(this.ownedDisposers).forEach((dispose) => dispose());
3044
3072
  }
3045
3073
  getContextValue(contextId, provideSymbol, defaultValue, hasDefault) {
3046
3074
  let computed2 = this.contextCache.get(contextId);
@@ -3290,12 +3318,6 @@ class Model {
3290
3318
  get parent() {
3291
3319
  return getModelAdm(this).parent?.proxy ?? null;
3292
3320
  }
3293
- reaction(track, callback) {
3294
- return getModelAdm(this).reaction(track, callback);
3295
- }
3296
- effect(callback) {
3297
- return getModelAdm(this).effect(callback);
3298
- }
3299
3321
  [Symbol.dispose]() {
3300
3322
  getModelAdm(this).dispose();
3301
3323
  }
@@ -13,7 +13,6 @@ export default class Store<PropsType extends Record<string, any> = StoreProps<Pr
13
13
  props: StoreProps<PropsType>;
14
14
  constructor(props: StoreProps<PropsType>);
15
15
  get key(): string | number | undefined;
16
- get isMounted(): boolean;
17
16
  reaction<T>(track: () => T, callback: (value: T, previousValue: T) => void): () => void;
18
17
  effect(callback: () => void | (() => void)): () => void;
19
18
  [Symbol.dispose](): void;
@@ -4,13 +4,17 @@ import type { StoreConfiguration, Props } from "../types";
4
4
  export declare function updateProps(props: Props, newProps: Props): void;
5
5
  export declare function getStoreAdm(store: Store): StoreAdministration;
6
6
  export declare class StoreAdministration<StoreType extends Store = Store> extends ObjectAdministration<Store> {
7
+ private static pendingActivations;
8
+ private static isFlushingActivations;
9
+ private static currentActivationDepth;
10
+ private static flushReactiveRegistrations;
7
11
  static proxyTraps: ProxyHandler<object>;
8
12
  parent: StoreAdministration | null;
9
- private mountedSignal;
13
+ private mounted;
10
14
  private disposed;
11
15
  private contextCache;
12
16
  private childStoreDataMap;
13
- private ownedDisposers;
17
+ private reactiveRegistrations;
14
18
  private configurationGetter?;
15
19
  setConfiguration(configurationGetter: () => StoreConfiguration<StoreType>): void;
16
20
  private get configuration();
@@ -26,9 +30,10 @@ export declare class StoreAdministration<StoreType extends Store = Store> extend
26
30
  get isMounted(): boolean;
27
31
  getContextValue<T>(contextId: symbol, provideSymbol: symbol, defaultValue: T | undefined, hasDefault: boolean): T;
28
32
  private lookupContextValue;
29
- private own;
33
+ private register;
34
+ private startReactiveRegistrations;
30
35
  reaction<T>(track: () => T, callback: (value: T, previousValue: T) => void): () => void;
31
36
  effect(callback: () => void | (() => void)): () => void;
32
- mount(parent?: StoreAdministration | null, childName?: PropertyKey): void;
37
+ mount(parent?: StoreAdministration | null, childName?: PropertyKey, activationQueue?: StoreAdministration[]): void;
33
38
  dispose(internal?: boolean): void;
34
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "r-state-tree",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "reactive state management library",
5
5
  "main": "dist/r-state-tree.cjs",
6
6
  "module": "dist/r-state-tree.js",