r-state-tree 0.4.1 → 0.4.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 +163 -8
- package/dist/decorators.d.ts +1 -1
- package/dist/r-state-tree.cjs +40 -40
- package/dist/r-state-tree.js +40 -40
- package/dist/store/Store.d.ts +8 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# r-state-tree
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
r-state-tree is a reactive state management library for building complex applications by moving state out of your render tree.
|
|
4
|
+
|
|
5
|
+
- Stores hold application/view state as a tree and drive your UI.
|
|
6
|
+
- Models hold domain state as a separate tree with snapshots, identifiers and references.
|
|
7
|
+
- Views become dumb renderers that react to Stores/Models.
|
|
4
8
|
|
|
5
9
|
## Installation
|
|
6
10
|
|
|
@@ -14,6 +18,13 @@ This library uses [TC39 Stage 3 Decorators](https://github.com/tc39/proposal-dec
|
|
|
14
18
|
|
|
15
19
|
The library includes a decorator metadata polyfill for runtimes that don't yet natively support `Symbol.metadata`.
|
|
16
20
|
|
|
21
|
+
## Core concepts
|
|
22
|
+
|
|
23
|
+
- Stores: application/view state containers. Create with `createStore()`, attach with `mount()`. Compose with `@child` (single or arrays, stable via `{ key }`). React to changes with `effect`/`reaction` and store lifecycles (`storeDidMount`/`storeWillUnmount`). Update reactive `props` via `updateStore()`.
|
|
24
|
+
- Models: domain state containers. Create with `Model.create()`. Persistent via snapshots (`toSnapshot`, `applySnapshot`, `onSnapshot`, diffs via `onSnapshotDiff`). Structure with `@state`, `@child`, identifiers via `@id`, and references via `@modelRef`.
|
|
25
|
+
- Context: pass data through Store/Model trees without prop drilling using `createContext<T>()`, `[Context.provide]`, and `Context.consume(this)`. Context is reactive and can be overridden by descendants.
|
|
26
|
+
- Reactivity: powered by signals. Use `@observable`, `@computed`, `effect`, `reaction`, `batch`, and `untracked` for precise updates.
|
|
27
|
+
|
|
17
28
|
## Stores
|
|
18
29
|
|
|
19
30
|
Stores describe reactive state containers composed into a tree.
|
|
@@ -143,6 +154,44 @@ batch(() => {
|
|
|
143
154
|
});
|
|
144
155
|
```
|
|
145
156
|
|
|
157
|
+
## Modeling guide
|
|
158
|
+
|
|
159
|
+
- Root store: mount a single root Store that composes the application via `@child` properties.
|
|
160
|
+
- View stores: create one Store per view/route/tab. Views render from stores; stores drive view transitions.
|
|
161
|
+
- Keyed children: pass `{ key }` when creating child stores to preserve identity across reorders.
|
|
162
|
+
- Models in stores: pass domain Models via `{ models }` and consume with `@model` on the Store.
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { Store, Model, child, createStore, mount } from "r-state-tree";
|
|
166
|
+
|
|
167
|
+
class TabViewStore extends Store<{ title: string }> {}
|
|
168
|
+
|
|
169
|
+
class RootStore extends Store {
|
|
170
|
+
@child get tabs() {
|
|
171
|
+
return ["Home", "Profile"].map((title, i) =>
|
|
172
|
+
createStore(TabViewStore, { title, key: i })
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const root = mount(createStore(RootStore));
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Passing models into stores:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
class User extends Model {
|
|
184
|
+
// ... @id, @state, etc.
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
class ProfileStore extends Store {
|
|
188
|
+
@model user: User; // injected model
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const user = User.create({ id: 1, name: "Alice" });
|
|
192
|
+
const profile = mount(createStore(ProfileStore, { models: { user } }));
|
|
193
|
+
```
|
|
194
|
+
|
|
146
195
|
## Observable classes
|
|
147
196
|
|
|
148
197
|
For reactive class instances outside the Model/Store system, use `@observable` and `@computed` decorators:
|
|
@@ -190,6 +239,61 @@ effect(() => {
|
|
|
190
239
|
state.count++; // Triggers the effect
|
|
191
240
|
```
|
|
192
241
|
|
|
242
|
+
## Observables (low‑level)
|
|
243
|
+
|
|
244
|
+
Create reactive structures outside Stores/Models. Supported: Objects, Arrays, Map, Set, WeakMap, WeakSet.
|
|
245
|
+
|
|
246
|
+
- Track reads with `effect`/`reaction`. Nested values are tracked when you access them inside your effects.
|
|
247
|
+
- Access raw values via `source(observable)`; check if something is reactive with `isObservable(value)`.
|
|
248
|
+
- Arrays: reading specific indices (`arr[i]`) or `length` tracks those; common mutators (`push/pop/shift/unshift/splice/reverse/sort/fill`) are reactive; non-index and symbol keys are not reactive.
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
import { observable, effect, computed, reaction } from "r-state-tree";
|
|
252
|
+
|
|
253
|
+
// Object
|
|
254
|
+
const state = observable({ count: 0, nested: { value: 1 } });
|
|
255
|
+
|
|
256
|
+
effect(() => {
|
|
257
|
+
// tracks reads
|
|
258
|
+
state.count;
|
|
259
|
+
state.nested.value;
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
state.count++;
|
|
263
|
+
state.nested.value++;
|
|
264
|
+
|
|
265
|
+
// Computed
|
|
266
|
+
const doubled = computed(() => state.count * 2);
|
|
267
|
+
effect(() => {
|
|
268
|
+
doubled.value;
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Array
|
|
272
|
+
const arr = observable([0, 1]);
|
|
273
|
+
effect(() => arr[0]);
|
|
274
|
+
arr[0]++; // triggers; arr.push(2) does not, index 0 didn't change
|
|
275
|
+
|
|
276
|
+
// Map
|
|
277
|
+
const map = observable(new Map([["k", 1]]));
|
|
278
|
+
effect(() => map.get("k"));
|
|
279
|
+
map.set("k", 2); // triggers
|
|
280
|
+
|
|
281
|
+
// Set
|
|
282
|
+
const set = observable(new Set([1]));
|
|
283
|
+
effect(() => set.has(2));
|
|
284
|
+
set.add(2); // triggers
|
|
285
|
+
|
|
286
|
+
// Reaction (runs only on changes, skips initial)
|
|
287
|
+
let last: number | undefined;
|
|
288
|
+
reaction(
|
|
289
|
+
() => state.count,
|
|
290
|
+
(v) => {
|
|
291
|
+
last = v;
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
state.count++;
|
|
295
|
+
```
|
|
296
|
+
|
|
193
297
|
## Models and snapshots
|
|
194
298
|
|
|
195
299
|
Models capture persistent state with snapshot utilities.
|
|
@@ -198,14 +302,14 @@ Models capture persistent state with snapshot utilities.
|
|
|
198
302
|
import {
|
|
199
303
|
Model,
|
|
200
304
|
state,
|
|
201
|
-
|
|
305
|
+
id,
|
|
202
306
|
applySnapshot,
|
|
203
307
|
onSnapshot,
|
|
204
308
|
toSnapshot,
|
|
205
309
|
} from "r-state-tree";
|
|
206
310
|
|
|
207
311
|
class TodoModel extends Model {
|
|
208
|
-
@
|
|
312
|
+
@id id = 0;
|
|
209
313
|
@state title = "";
|
|
210
314
|
@state completed = false;
|
|
211
315
|
}
|
|
@@ -251,15 +355,15 @@ class TodoModel extends Model {
|
|
|
251
355
|
Use decorators to configure model properties:
|
|
252
356
|
|
|
253
357
|
```ts
|
|
254
|
-
import { Model, state,
|
|
358
|
+
import { Model, state, id, child, modelRef } from "r-state-tree";
|
|
255
359
|
|
|
256
360
|
class User extends Model {
|
|
257
|
-
@
|
|
361
|
+
@id id = 0;
|
|
258
362
|
@state name = "";
|
|
259
363
|
}
|
|
260
364
|
|
|
261
365
|
class TodoModel extends Model {
|
|
262
|
-
@
|
|
366
|
+
@id id = 0;
|
|
263
367
|
@state title = "";
|
|
264
368
|
@modelRef assignee?: User; // Reference to another model by ID
|
|
265
369
|
@child metadata = MetadataModel.create(); // Nested child model
|
|
@@ -282,7 +386,7 @@ Reference models by ID using `@modelRef`:
|
|
|
282
386
|
|
|
283
387
|
```ts
|
|
284
388
|
class ProjectModel extends Model {
|
|
285
|
-
@
|
|
389
|
+
@id id = 0;
|
|
286
390
|
@child users: User[] = [];
|
|
287
391
|
@modelRef owner?: User; // Single reference
|
|
288
392
|
@modelRef assignees: User[] = []; // Array of references
|
|
@@ -310,7 +414,7 @@ Both `@child` and `@modelRef` support runtime type switching between single valu
|
|
|
310
414
|
|
|
311
415
|
```ts
|
|
312
416
|
class ItemModel extends Model {
|
|
313
|
-
@
|
|
417
|
+
@id id = 0;
|
|
314
418
|
@state value = 0;
|
|
315
419
|
}
|
|
316
420
|
|
|
@@ -330,6 +434,57 @@ class ContainerModel extends Model {
|
|
|
330
434
|
}
|
|
331
435
|
```
|
|
332
436
|
|
|
437
|
+
## API surface
|
|
438
|
+
|
|
439
|
+
- Stores
|
|
440
|
+
- `Store`, `createStore`, `mount`, `unmount`, `updateStore`
|
|
441
|
+
- Models
|
|
442
|
+
- `Model`, decorators: `@state`, `@id`, `@child`, `@modelRef`
|
|
443
|
+
- Snapshots
|
|
444
|
+
- `onSnapshot`, `toSnapshot`, `applySnapshot`, `onSnapshotDiff`
|
|
445
|
+
- Types: `Snapshot`, `SnapshotDiff`, `IdType`, `Configuration`
|
|
446
|
+
- Context
|
|
447
|
+
- `createContext`, type `Context`
|
|
448
|
+
- Reactivity and observables
|
|
449
|
+
- `observable`, `computed`, `effect`, `reaction`, `batch`, `untracked`, `Observable`
|
|
450
|
+
- Utilities: `isObservable`, `source`, `reportObserved`, `reportChanged`
|
|
451
|
+
- Signals interop
|
|
452
|
+
- `signal`, `getSignal`, types `Signal`, `ReadonlySignal`
|
|
453
|
+
|
|
454
|
+
## UI integration and signals interop
|
|
455
|
+
|
|
456
|
+
r-state-tree is built on `@preact/signals-core`. You can interoperate with signals directly:
|
|
457
|
+
|
|
458
|
+
- Per-property signals via `$prop` on observable objects (including Stores/Models), or `getSignal(obj, key)`.
|
|
459
|
+
- Re-exported utilities: `signal`, `computed`, `effect`, `batch`, `untracked`, and types `Signal`, `ReadonlySignal`.
|
|
460
|
+
|
|
461
|
+
```ts
|
|
462
|
+
import { observable, effect, getSignal } from "r-state-tree";
|
|
463
|
+
|
|
464
|
+
const state = observable({ count: 0 });
|
|
465
|
+
|
|
466
|
+
// Either form returns a Signal<number>
|
|
467
|
+
const s1 = state.$count;
|
|
468
|
+
const s2 = getSignal(state, "count");
|
|
469
|
+
|
|
470
|
+
effect(() => {
|
|
471
|
+
// Use s1.value (or s2.value) in your UI binding
|
|
472
|
+
console.log("count:", s1.value);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// Update via signal or through the object
|
|
476
|
+
s1.value = 1;
|
|
477
|
+
state.count = 2;
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Identifier and reference rules
|
|
481
|
+
|
|
482
|
+
- `@id` values are unique within a tree. They cannot be cleared to `undefined` after assignment.
|
|
483
|
+
- Identifiers can be reassigned to a new value (including in snapshots) as long as uniqueness is preserved.
|
|
484
|
+
- `@modelRef` requires the referenced model to have an id and be attached to the tree; the ref becomes `undefined` when the model detaches.
|
|
485
|
+
- When a model is re-attached to the same tree, compatible refs restore automatically; attaching to a different root does not restore prior refs.
|
|
486
|
+
- `@modelRef` and `@child` can switch between single and array at runtime; reactions observe the property itself rather than internal array mutations.
|
|
487
|
+
|
|
333
488
|
### Snapshot diffs
|
|
334
489
|
|
|
335
490
|
Use `onSnapshotDiff` to receive undo/redo payloads:
|
package/dist/decorators.d.ts
CHANGED
package/dist/r-state-tree.cjs
CHANGED
|
@@ -1672,13 +1672,13 @@ function onSnapshotLoad(fn) {
|
|
|
1672
1672
|
rootMap.set(root, set);
|
|
1673
1673
|
}
|
|
1674
1674
|
if (idMap.has(model2)) {
|
|
1675
|
-
const
|
|
1676
|
-
if (set.has(
|
|
1675
|
+
const id2 = idMap.get(model2);
|
|
1676
|
+
if (set.has(id2)) {
|
|
1677
1677
|
throw new Error(
|
|
1678
1678
|
"r-state-tree duplicate ids detected after snapshot was loaded"
|
|
1679
1679
|
);
|
|
1680
1680
|
}
|
|
1681
|
-
set.add(
|
|
1681
|
+
set.add(id2);
|
|
1682
1682
|
}
|
|
1683
1683
|
});
|
|
1684
1684
|
} finally {
|
|
@@ -1687,40 +1687,40 @@ function onSnapshotLoad(fn) {
|
|
|
1687
1687
|
}
|
|
1688
1688
|
}
|
|
1689
1689
|
}
|
|
1690
|
-
function setIdentifier(model2,
|
|
1690
|
+
function setIdentifier(model2, id2) {
|
|
1691
1691
|
const prevId = idMap.get(model2);
|
|
1692
|
-
idMap.set(model2,
|
|
1692
|
+
idMap.set(model2, id2);
|
|
1693
1693
|
if (prevId == null) {
|
|
1694
1694
|
if (model2.parent) {
|
|
1695
1695
|
onModelAttached(model2);
|
|
1696
1696
|
}
|
|
1697
|
-
} else if (prevId !==
|
|
1697
|
+
} else if (prevId !== id2) {
|
|
1698
1698
|
updateIdentifier(model2);
|
|
1699
1699
|
}
|
|
1700
1700
|
}
|
|
1701
1701
|
function getIdentifier(model2) {
|
|
1702
1702
|
return idMap.get(model2);
|
|
1703
1703
|
}
|
|
1704
|
-
function getModelById(root,
|
|
1704
|
+
function getModelById(root, id2) {
|
|
1705
1705
|
const map = attachedIdMap.get(root);
|
|
1706
|
-
return map?.get(
|
|
1706
|
+
return map?.get(id2);
|
|
1707
1707
|
}
|
|
1708
1708
|
function updateIdentifier(model2) {
|
|
1709
|
-
const
|
|
1709
|
+
const id2 = idMap.get(model2);
|
|
1710
1710
|
let node = model2.parent;
|
|
1711
1711
|
while (node) {
|
|
1712
1712
|
const map = attachedIdMap.get(node);
|
|
1713
1713
|
if (map) {
|
|
1714
|
-
map.set(
|
|
1714
|
+
map.set(id2, model2);
|
|
1715
1715
|
}
|
|
1716
1716
|
node = node.parent;
|
|
1717
1717
|
}
|
|
1718
1718
|
}
|
|
1719
1719
|
function onModelAttached(model2) {
|
|
1720
1720
|
const attachedMap = attachedIdMap.get(model2);
|
|
1721
|
-
const
|
|
1722
|
-
if (attachedMap ||
|
|
1723
|
-
const
|
|
1721
|
+
const id2 = idMap.get(model2);
|
|
1722
|
+
if (attachedMap || id2 != null) {
|
|
1723
|
+
const id22 = idMap.get(model2);
|
|
1724
1724
|
let node = model2.parent;
|
|
1725
1725
|
while (node) {
|
|
1726
1726
|
let map = attachedIdMap.get(node);
|
|
@@ -1741,26 +1741,26 @@ function onModelAttached(model2) {
|
|
|
1741
1741
|
}
|
|
1742
1742
|
map.set(key, value);
|
|
1743
1743
|
});
|
|
1744
|
-
if (
|
|
1745
|
-
if (map.has(
|
|
1744
|
+
if (id22 != null) {
|
|
1745
|
+
if (map.has(id22)) {
|
|
1746
1746
|
if (loadingSnapshot) {
|
|
1747
1747
|
potentialDups.add(model2);
|
|
1748
|
-
potentialDups.add(map.get(
|
|
1748
|
+
potentialDups.add(map.get(id22));
|
|
1749
1749
|
} else {
|
|
1750
1750
|
throw new Error(
|
|
1751
|
-
`r-state-tree: id: ${
|
|
1751
|
+
`r-state-tree: id: ${id22} is already assigned to another model`
|
|
1752
1752
|
);
|
|
1753
1753
|
}
|
|
1754
1754
|
}
|
|
1755
|
-
map.set(
|
|
1755
|
+
map.set(id22, model2);
|
|
1756
1756
|
}
|
|
1757
1757
|
node = node.parent;
|
|
1758
1758
|
}
|
|
1759
1759
|
}
|
|
1760
1760
|
}
|
|
1761
1761
|
function onModelDetached(model2) {
|
|
1762
|
-
const
|
|
1763
|
-
if (attachedIdMap.has(model2) ||
|
|
1762
|
+
const id2 = idMap.get(model2);
|
|
1763
|
+
if (attachedIdMap.has(model2) || id2 != null) {
|
|
1764
1764
|
const attachedMap = attachedIdMap.get(model2);
|
|
1765
1765
|
let node = model2.parent;
|
|
1766
1766
|
while (node) {
|
|
@@ -1769,8 +1769,8 @@ function onModelDetached(model2) {
|
|
|
1769
1769
|
attachedMap?.forEach((value, key) => {
|
|
1770
1770
|
map.delete(key);
|
|
1771
1771
|
});
|
|
1772
|
-
if (
|
|
1773
|
-
map.delete(
|
|
1772
|
+
if (id2 != null) {
|
|
1773
|
+
map.delete(id2);
|
|
1774
1774
|
}
|
|
1775
1775
|
}
|
|
1776
1776
|
node = node.parent;
|
|
@@ -2015,13 +2015,13 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2015
2015
|
}
|
|
2016
2016
|
}
|
|
2017
2017
|
setId(name, v) {
|
|
2018
|
-
const
|
|
2019
|
-
if (
|
|
2018
|
+
const id2 = getIdentifier(this.proxy);
|
|
2019
|
+
if (id2 === v) {
|
|
2020
2020
|
return;
|
|
2021
2021
|
}
|
|
2022
|
-
if (
|
|
2022
|
+
if (id2 != null && v == null) {
|
|
2023
2023
|
throw new Error(
|
|
2024
|
-
"r-state-tree can't clear an
|
|
2024
|
+
"r-state-tree can't clear an id once it has already been set."
|
|
2025
2025
|
);
|
|
2026
2026
|
}
|
|
2027
2027
|
if (v !== void 0) {
|
|
@@ -2107,7 +2107,7 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2107
2107
|
if (!this.referencedModels) this.referencedModels = /* @__PURE__ */ new Map();
|
|
2108
2108
|
c = createComputed(() => {
|
|
2109
2109
|
a.reportObserved();
|
|
2110
|
-
const models = (this.source[name] || []).map((
|
|
2110
|
+
const models = (this.source[name] || []).map((id2) => getModelById(this.root.proxy, id2)).filter((m) => !!m);
|
|
2111
2111
|
return models;
|
|
2112
2112
|
});
|
|
2113
2113
|
this.referencedModels.set(name, c);
|
|
@@ -2115,27 +2115,27 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2115
2115
|
return c.get();
|
|
2116
2116
|
}
|
|
2117
2117
|
setModelRef(name, modelValue) {
|
|
2118
|
-
let
|
|
2118
|
+
let id2 = void 0;
|
|
2119
2119
|
if (modelValue) {
|
|
2120
|
-
|
|
2121
|
-
if (
|
|
2120
|
+
id2 = getIdentifier(modelValue);
|
|
2121
|
+
if (id2 == null) {
|
|
2122
2122
|
throw new Error(
|
|
2123
2123
|
"r-state-tree: Only models with identifiers can be used as a ref"
|
|
2124
2124
|
);
|
|
2125
2125
|
}
|
|
2126
2126
|
}
|
|
2127
|
-
this.source[name] =
|
|
2127
|
+
this.source[name] = id2;
|
|
2128
2128
|
this.referencedAtoms?.get(name)?.reportChanged();
|
|
2129
2129
|
}
|
|
2130
2130
|
setModelRefs(name, modelValue) {
|
|
2131
2131
|
const ids = modelValue.map((model2) => {
|
|
2132
|
-
const
|
|
2133
|
-
if (
|
|
2132
|
+
const id2 = getIdentifier(model2);
|
|
2133
|
+
if (id2 == null) {
|
|
2134
2134
|
throw new Error(
|
|
2135
2135
|
"r-state-tree: Only models with identifiers can be used as a ref"
|
|
2136
2136
|
);
|
|
2137
2137
|
}
|
|
2138
|
-
return
|
|
2138
|
+
return id2;
|
|
2139
2139
|
});
|
|
2140
2140
|
this.source[name] = ids;
|
|
2141
2141
|
this.referencedAtoms?.get(name)?.reportChanged();
|
|
@@ -2304,8 +2304,8 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2304
2304
|
model22 = snapshot2;
|
|
2305
2305
|
} else {
|
|
2306
2306
|
ensureChildTypes(key);
|
|
2307
|
-
const
|
|
2308
|
-
const foundModel =
|
|
2307
|
+
const id2 = childType2 && getSnapshotId(snapshot2, Ctor);
|
|
2308
|
+
const foundModel = id2 != null ? getModelById(this.root.proxy, id2) : this.proxy[key][index];
|
|
2309
2309
|
const adm = foundModel && getModelAdm(foundModel);
|
|
2310
2310
|
if (adm && foundModel.parent === this.proxy && adm?.parentName === key) {
|
|
2311
2311
|
adm.loadSnapshot(snapshot2);
|
|
@@ -2322,8 +2322,8 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2322
2322
|
model2 = value;
|
|
2323
2323
|
} else {
|
|
2324
2324
|
ensureChildTypes(key);
|
|
2325
|
-
const
|
|
2326
|
-
if (
|
|
2325
|
+
const id2 = childType2 && getSnapshotId(value, childType2);
|
|
2326
|
+
if (id2 != null && this.proxy[key] && id2 === getIdentifier(this.proxy[key])) {
|
|
2327
2327
|
const adm = getModelAdm(this.proxy[key]);
|
|
2328
2328
|
adm.loadSnapshot(value);
|
|
2329
2329
|
model2 = this.proxy[key];
|
|
@@ -2483,7 +2483,7 @@ function makeChildDecorator(typeObj) {
|
|
|
2483
2483
|
const child = makeChildDecorator(childType);
|
|
2484
2484
|
const modelRef = makeChildDecorator(modelRefType);
|
|
2485
2485
|
const model = makeDecorator(modelType);
|
|
2486
|
-
const
|
|
2486
|
+
const id = makeDecorator(idType);
|
|
2487
2487
|
const state = makeDecorator(stateType);
|
|
2488
2488
|
Object.defineProperty(exports, "ReadonlySignal", {
|
|
2489
2489
|
enumerable: true,
|
|
@@ -2518,7 +2518,7 @@ exports.computed = computed;
|
|
|
2518
2518
|
exports.createContext = createContext;
|
|
2519
2519
|
exports.createStore = createStore;
|
|
2520
2520
|
exports.getSignal = getSignal;
|
|
2521
|
-
exports.
|
|
2521
|
+
exports.id = id;
|
|
2522
2522
|
exports.isObservable = isObservable;
|
|
2523
2523
|
exports.model = model;
|
|
2524
2524
|
exports.modelRef = modelRef;
|
package/dist/r-state-tree.js
CHANGED
|
@@ -1671,13 +1671,13 @@ function onSnapshotLoad(fn) {
|
|
|
1671
1671
|
rootMap.set(root, set);
|
|
1672
1672
|
}
|
|
1673
1673
|
if (idMap.has(model2)) {
|
|
1674
|
-
const
|
|
1675
|
-
if (set.has(
|
|
1674
|
+
const id2 = idMap.get(model2);
|
|
1675
|
+
if (set.has(id2)) {
|
|
1676
1676
|
throw new Error(
|
|
1677
1677
|
"r-state-tree duplicate ids detected after snapshot was loaded"
|
|
1678
1678
|
);
|
|
1679
1679
|
}
|
|
1680
|
-
set.add(
|
|
1680
|
+
set.add(id2);
|
|
1681
1681
|
}
|
|
1682
1682
|
});
|
|
1683
1683
|
} finally {
|
|
@@ -1686,40 +1686,40 @@ function onSnapshotLoad(fn) {
|
|
|
1686
1686
|
}
|
|
1687
1687
|
}
|
|
1688
1688
|
}
|
|
1689
|
-
function setIdentifier(model2,
|
|
1689
|
+
function setIdentifier(model2, id2) {
|
|
1690
1690
|
const prevId = idMap.get(model2);
|
|
1691
|
-
idMap.set(model2,
|
|
1691
|
+
idMap.set(model2, id2);
|
|
1692
1692
|
if (prevId == null) {
|
|
1693
1693
|
if (model2.parent) {
|
|
1694
1694
|
onModelAttached(model2);
|
|
1695
1695
|
}
|
|
1696
|
-
} else if (prevId !==
|
|
1696
|
+
} else if (prevId !== id2) {
|
|
1697
1697
|
updateIdentifier(model2);
|
|
1698
1698
|
}
|
|
1699
1699
|
}
|
|
1700
1700
|
function getIdentifier(model2) {
|
|
1701
1701
|
return idMap.get(model2);
|
|
1702
1702
|
}
|
|
1703
|
-
function getModelById(root,
|
|
1703
|
+
function getModelById(root, id2) {
|
|
1704
1704
|
const map = attachedIdMap.get(root);
|
|
1705
|
-
return map?.get(
|
|
1705
|
+
return map?.get(id2);
|
|
1706
1706
|
}
|
|
1707
1707
|
function updateIdentifier(model2) {
|
|
1708
|
-
const
|
|
1708
|
+
const id2 = idMap.get(model2);
|
|
1709
1709
|
let node = model2.parent;
|
|
1710
1710
|
while (node) {
|
|
1711
1711
|
const map = attachedIdMap.get(node);
|
|
1712
1712
|
if (map) {
|
|
1713
|
-
map.set(
|
|
1713
|
+
map.set(id2, model2);
|
|
1714
1714
|
}
|
|
1715
1715
|
node = node.parent;
|
|
1716
1716
|
}
|
|
1717
1717
|
}
|
|
1718
1718
|
function onModelAttached(model2) {
|
|
1719
1719
|
const attachedMap = attachedIdMap.get(model2);
|
|
1720
|
-
const
|
|
1721
|
-
if (attachedMap ||
|
|
1722
|
-
const
|
|
1720
|
+
const id2 = idMap.get(model2);
|
|
1721
|
+
if (attachedMap || id2 != null) {
|
|
1722
|
+
const id22 = idMap.get(model2);
|
|
1723
1723
|
let node = model2.parent;
|
|
1724
1724
|
while (node) {
|
|
1725
1725
|
let map = attachedIdMap.get(node);
|
|
@@ -1740,26 +1740,26 @@ function onModelAttached(model2) {
|
|
|
1740
1740
|
}
|
|
1741
1741
|
map.set(key, value);
|
|
1742
1742
|
});
|
|
1743
|
-
if (
|
|
1744
|
-
if (map.has(
|
|
1743
|
+
if (id22 != null) {
|
|
1744
|
+
if (map.has(id22)) {
|
|
1745
1745
|
if (loadingSnapshot) {
|
|
1746
1746
|
potentialDups.add(model2);
|
|
1747
|
-
potentialDups.add(map.get(
|
|
1747
|
+
potentialDups.add(map.get(id22));
|
|
1748
1748
|
} else {
|
|
1749
1749
|
throw new Error(
|
|
1750
|
-
`r-state-tree: id: ${
|
|
1750
|
+
`r-state-tree: id: ${id22} is already assigned to another model`
|
|
1751
1751
|
);
|
|
1752
1752
|
}
|
|
1753
1753
|
}
|
|
1754
|
-
map.set(
|
|
1754
|
+
map.set(id22, model2);
|
|
1755
1755
|
}
|
|
1756
1756
|
node = node.parent;
|
|
1757
1757
|
}
|
|
1758
1758
|
}
|
|
1759
1759
|
}
|
|
1760
1760
|
function onModelDetached(model2) {
|
|
1761
|
-
const
|
|
1762
|
-
if (attachedIdMap.has(model2) ||
|
|
1761
|
+
const id2 = idMap.get(model2);
|
|
1762
|
+
if (attachedIdMap.has(model2) || id2 != null) {
|
|
1763
1763
|
const attachedMap = attachedIdMap.get(model2);
|
|
1764
1764
|
let node = model2.parent;
|
|
1765
1765
|
while (node) {
|
|
@@ -1768,8 +1768,8 @@ function onModelDetached(model2) {
|
|
|
1768
1768
|
attachedMap?.forEach((value, key) => {
|
|
1769
1769
|
map.delete(key);
|
|
1770
1770
|
});
|
|
1771
|
-
if (
|
|
1772
|
-
map.delete(
|
|
1771
|
+
if (id2 != null) {
|
|
1772
|
+
map.delete(id2);
|
|
1773
1773
|
}
|
|
1774
1774
|
}
|
|
1775
1775
|
node = node.parent;
|
|
@@ -2014,13 +2014,13 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2014
2014
|
}
|
|
2015
2015
|
}
|
|
2016
2016
|
setId(name, v) {
|
|
2017
|
-
const
|
|
2018
|
-
if (
|
|
2017
|
+
const id2 = getIdentifier(this.proxy);
|
|
2018
|
+
if (id2 === v) {
|
|
2019
2019
|
return;
|
|
2020
2020
|
}
|
|
2021
|
-
if (
|
|
2021
|
+
if (id2 != null && v == null) {
|
|
2022
2022
|
throw new Error(
|
|
2023
|
-
"r-state-tree can't clear an
|
|
2023
|
+
"r-state-tree can't clear an id once it has already been set."
|
|
2024
2024
|
);
|
|
2025
2025
|
}
|
|
2026
2026
|
if (v !== void 0) {
|
|
@@ -2106,7 +2106,7 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2106
2106
|
if (!this.referencedModels) this.referencedModels = /* @__PURE__ */ new Map();
|
|
2107
2107
|
c = createComputed(() => {
|
|
2108
2108
|
a.reportObserved();
|
|
2109
|
-
const models = (this.source[name] || []).map((
|
|
2109
|
+
const models = (this.source[name] || []).map((id2) => getModelById(this.root.proxy, id2)).filter((m) => !!m);
|
|
2110
2110
|
return models;
|
|
2111
2111
|
});
|
|
2112
2112
|
this.referencedModels.set(name, c);
|
|
@@ -2114,27 +2114,27 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2114
2114
|
return c.get();
|
|
2115
2115
|
}
|
|
2116
2116
|
setModelRef(name, modelValue) {
|
|
2117
|
-
let
|
|
2117
|
+
let id2 = void 0;
|
|
2118
2118
|
if (modelValue) {
|
|
2119
|
-
|
|
2120
|
-
if (
|
|
2119
|
+
id2 = getIdentifier(modelValue);
|
|
2120
|
+
if (id2 == null) {
|
|
2121
2121
|
throw new Error(
|
|
2122
2122
|
"r-state-tree: Only models with identifiers can be used as a ref"
|
|
2123
2123
|
);
|
|
2124
2124
|
}
|
|
2125
2125
|
}
|
|
2126
|
-
this.source[name] =
|
|
2126
|
+
this.source[name] = id2;
|
|
2127
2127
|
this.referencedAtoms?.get(name)?.reportChanged();
|
|
2128
2128
|
}
|
|
2129
2129
|
setModelRefs(name, modelValue) {
|
|
2130
2130
|
const ids = modelValue.map((model2) => {
|
|
2131
|
-
const
|
|
2132
|
-
if (
|
|
2131
|
+
const id2 = getIdentifier(model2);
|
|
2132
|
+
if (id2 == null) {
|
|
2133
2133
|
throw new Error(
|
|
2134
2134
|
"r-state-tree: Only models with identifiers can be used as a ref"
|
|
2135
2135
|
);
|
|
2136
2136
|
}
|
|
2137
|
-
return
|
|
2137
|
+
return id2;
|
|
2138
2138
|
});
|
|
2139
2139
|
this.source[name] = ids;
|
|
2140
2140
|
this.referencedAtoms?.get(name)?.reportChanged();
|
|
@@ -2303,8 +2303,8 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2303
2303
|
model22 = snapshot2;
|
|
2304
2304
|
} else {
|
|
2305
2305
|
ensureChildTypes(key);
|
|
2306
|
-
const
|
|
2307
|
-
const foundModel =
|
|
2306
|
+
const id2 = childType2 && getSnapshotId(snapshot2, Ctor);
|
|
2307
|
+
const foundModel = id2 != null ? getModelById(this.root.proxy, id2) : this.proxy[key][index];
|
|
2308
2308
|
const adm = foundModel && getModelAdm(foundModel);
|
|
2309
2309
|
if (adm && foundModel.parent === this.proxy && adm?.parentName === key) {
|
|
2310
2310
|
adm.loadSnapshot(snapshot2);
|
|
@@ -2321,8 +2321,8 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2321
2321
|
model2 = value;
|
|
2322
2322
|
} else {
|
|
2323
2323
|
ensureChildTypes(key);
|
|
2324
|
-
const
|
|
2325
|
-
if (
|
|
2324
|
+
const id2 = childType2 && getSnapshotId(value, childType2);
|
|
2325
|
+
if (id2 != null && this.proxy[key] && id2 === getIdentifier(this.proxy[key])) {
|
|
2326
2326
|
const adm = getModelAdm(this.proxy[key]);
|
|
2327
2327
|
adm.loadSnapshot(value);
|
|
2328
2328
|
model2 = this.proxy[key];
|
|
@@ -2482,7 +2482,7 @@ function makeChildDecorator(typeObj) {
|
|
|
2482
2482
|
const child = makeChildDecorator(childType);
|
|
2483
2483
|
const modelRef = makeChildDecorator(modelRefType);
|
|
2484
2484
|
const model = makeDecorator(modelType);
|
|
2485
|
-
const
|
|
2485
|
+
const id = makeDecorator(idType);
|
|
2486
2486
|
const state = makeDecorator(stateType);
|
|
2487
2487
|
export {
|
|
2488
2488
|
Model,
|
|
@@ -2498,7 +2498,7 @@ export {
|
|
|
2498
2498
|
createStore,
|
|
2499
2499
|
effect2 as effect,
|
|
2500
2500
|
getSignal,
|
|
2501
|
-
|
|
2501
|
+
id,
|
|
2502
2502
|
isObservable,
|
|
2503
2503
|
model,
|
|
2504
2504
|
modelRef,
|
package/dist/store/Store.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { Props, StoreConfiguration } from "../types";
|
|
2
2
|
export declare function allowNewStore<T>(fn: () => T): T;
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
type CreateStoreProps<T extends Props> = {
|
|
4
|
+
[K in keyof T as undefined extends T[K] ? K : never]?: T[K] extends infer U ? U extends undefined ? never : undefined extends U ? U | undefined : U : never;
|
|
5
|
+
} & {
|
|
6
|
+
[K in keyof T as undefined extends T[K] ? never : K]?: T[K];
|
|
7
|
+
} & Pick<Props, 'key'>;
|
|
8
|
+
export declare function createStore<K extends Store<T>, T extends Props>(Type: new (props: T) => K, props?: CreateStoreProps<T>): K;
|
|
9
|
+
export declare function updateStore<K extends Store<T>, T extends Props>(store: K, props: CreateStoreProps<T>): K;
|
|
5
10
|
export declare function types<T extends Store>(config: Partial<StoreConfiguration<T>>): Partial<StoreConfiguration<T>>;
|
|
6
11
|
export default class Store<PropsType extends Props = Props> {
|
|
7
12
|
static get types(): StoreConfiguration<unknown>;
|
|
@@ -12,3 +17,4 @@ export default class Store<PropsType extends Props = Props> {
|
|
|
12
17
|
storeDidMount(): void;
|
|
13
18
|
storeWillUnmount(): void;
|
|
14
19
|
}
|
|
20
|
+
export {};
|