@signaltree/core 7.6.0 → 7.6.1
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 +44 -43
- package/dist/enhancers/devtools/devtools.js +24 -10
- package/dist/lib/utils.js +22 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -118,7 +118,7 @@ Performance and bundle size vary by app shape, build tooling, device, and runtim
|
|
|
118
118
|
|
|
119
119
|
## Best Practices (SignalTree-First)
|
|
120
120
|
|
|
121
|
-
> 📖 **Full guide**: [
|
|
121
|
+
> 📖 **Full guide**: [Architecture Guide](https://github.com/JBorgia/signaltree/blob/main/docs/architecture/signaltree-architecture-guide.md)
|
|
122
122
|
|
|
123
123
|
Follow these principles for idiomatic SignalTree code:
|
|
124
124
|
|
|
@@ -164,7 +164,7 @@ export function createUserTree() {
|
|
|
164
164
|
// ✅ Correct: Derived from multiple signals
|
|
165
165
|
const selectedUser = computed(() => {
|
|
166
166
|
const id = $.selected.userId();
|
|
167
|
-
return id ? $.users.byId(id)() : null;
|
|
167
|
+
return id ? $.users.byId(id)?.() ?? null : null;
|
|
168
168
|
});
|
|
169
169
|
|
|
170
170
|
// ❌ Wrong: Wrapping an existing signal
|
|
@@ -175,8 +175,8 @@ const selectedUserId = computed(() => $.selected.userId()); // Unnecessary!
|
|
|
175
175
|
|
|
176
176
|
```typescript
|
|
177
177
|
// ✅ SignalTree-native
|
|
178
|
-
const user = $.users.byId(123)(); // O(1) lookup
|
|
179
|
-
const allUsers = $.users.all; // Get all
|
|
178
|
+
const user = $.users.byId(123)?.(); // O(1) lookup
|
|
179
|
+
const allUsers = $.users.all(); // Get all
|
|
180
180
|
$.users.setAll(usersFromApi); // Replace all
|
|
181
181
|
|
|
182
182
|
// ❌ NgRx-style (avoid)
|
|
@@ -650,7 +650,7 @@ All enhancers are exported directly from `@signaltree/core`:
|
|
|
650
650
|
|
|
651
651
|
**Data Management:**
|
|
652
652
|
|
|
653
|
-
- `entities()` -
|
|
653
|
+
- `entities()` - Legacy helper (not required when using `entityMap()` in v7)
|
|
654
654
|
- `createAsyncOperation()` - Async operation management with loading/error states
|
|
655
655
|
- `trackAsync()` - Track async operations in your state
|
|
656
656
|
- `serialization()` - State persistence and SSR support
|
|
@@ -658,7 +658,7 @@ All enhancers are exported directly from `@signaltree/core`:
|
|
|
658
658
|
|
|
659
659
|
**Development Tools:**
|
|
660
660
|
|
|
661
|
-
- `devTools()` - Redux DevTools
|
|
661
|
+
- `devTools()` - Redux DevTools auto-connect, path actions, and time-travel dispatch
|
|
662
662
|
- `withTimeTravel()` - Undo/redo functionality
|
|
663
663
|
|
|
664
664
|
**Presets:**
|
|
@@ -691,14 +691,12 @@ const tree = signalTree({ count: 0 }).with(
|
|
|
691
691
|
**Performance-Focused Stack:**
|
|
692
692
|
|
|
693
693
|
```typescript
|
|
694
|
-
import { signalTree, batching, memoization,
|
|
694
|
+
import { signalTree, batching, memoization, entityMap } from '@signaltree/core';
|
|
695
695
|
|
|
696
696
|
const tree = signalTree({
|
|
697
697
|
products: entityMap<Product>(),
|
|
698
698
|
ui: { loading: false },
|
|
699
|
-
})
|
|
700
|
-
.with(entities()) // Efficient CRUD operations (auto-detects entityMap)
|
|
701
|
-
.with(batching()); // Batch updates for optimal rendering
|
|
699
|
+
}).with(batching()); // Batch updates for optimal rendering
|
|
702
700
|
|
|
703
701
|
// Entity CRUD operations
|
|
704
702
|
tree.$.products.addOne(newProduct);
|
|
@@ -748,6 +746,8 @@ tree.undo(); // Revert changes
|
|
|
748
746
|
|
|
749
747
|
#### Enhancer Metadata & Ordering
|
|
750
748
|
|
|
749
|
+
Derived computed signals are preserved across `.with()` chaining, so enhancer composition does not recreate signal identities.
|
|
750
|
+
|
|
751
751
|
Enhancers can declare metadata for automatic dependency resolution:
|
|
752
752
|
|
|
753
753
|
```typescript
|
|
@@ -782,20 +782,20 @@ const customTree = signalTree(state, TREE_PRESETS.DASHBOARD);
|
|
|
782
782
|
SignalTree Core includes all enhancer functionality built-in. No separate packages needed:
|
|
783
783
|
|
|
784
784
|
```typescript
|
|
785
|
-
import { signalTree, entityMap
|
|
785
|
+
import { signalTree, entityMap } from '@signaltree/core';
|
|
786
786
|
|
|
787
787
|
// Without entityMap - use manual array updates
|
|
788
788
|
const basic = signalTree({ users: [] as User[] });
|
|
789
789
|
basic.$.users.update((users) => [...users, newUser]);
|
|
790
790
|
|
|
791
|
-
// With entityMap +
|
|
791
|
+
// With entityMap (v7+ auto-processed) - use entity helpers
|
|
792
792
|
const enhanced = signalTree({
|
|
793
793
|
users: entityMap<User>(),
|
|
794
|
-
})
|
|
794
|
+
});
|
|
795
795
|
|
|
796
796
|
enhanced.$.users.addOne(newUser); // ✅ Advanced CRUD operations
|
|
797
|
-
enhanced.$.users.byId(123)(); // ✅ O(1) lookups
|
|
798
|
-
enhanced.$.users.all; // ✅ Get all as array
|
|
797
|
+
enhanced.$.users.byId(123)?.(); // ✅ O(1) lookups
|
|
798
|
+
enhanced.$.users.all(); // ✅ Get all as array
|
|
799
799
|
```
|
|
800
800
|
|
|
801
801
|
Core includes several performance optimizations:
|
|
@@ -908,7 +908,7 @@ const tree = signalTree({ count: 0 }).with(withLogger());
|
|
|
908
908
|
tree.log('Tree created');
|
|
909
909
|
```
|
|
910
910
|
|
|
911
|
-
> 📖 **Full guide**: [Custom Markers & Enhancers](https://github.com/JBorgia/signaltree/blob/main/docs/custom-markers-enhancers.md)
|
|
911
|
+
> 📖 **Full guide**: [Custom Markers & Enhancers](https://github.com/JBorgia/signaltree/blob/main/docs/guides/custom-markers-enhancers.md)
|
|
912
912
|
>
|
|
913
913
|
> 📱 **Interactive demo**: [Demo App](/custom-extensions)
|
|
914
914
|
|
|
@@ -1574,19 +1574,19 @@ const filteredProducts = computed(() => {
|
|
|
1574
1574
|
### Data Management Composition
|
|
1575
1575
|
|
|
1576
1576
|
```typescript
|
|
1577
|
-
import { signalTree, entityMap
|
|
1577
|
+
import { signalTree, entityMap } from '@signaltree/core';
|
|
1578
1578
|
|
|
1579
|
-
// Add data management capabilities (
|
|
1579
|
+
// Add data management capabilities (entityMap is auto-processed)
|
|
1580
1580
|
const tree = signalTree({
|
|
1581
1581
|
users: entityMap<User>(),
|
|
1582
1582
|
posts: entityMap<Post>(),
|
|
1583
1583
|
ui: { loading: false, error: null as string | null },
|
|
1584
|
-
})
|
|
1584
|
+
});
|
|
1585
1585
|
|
|
1586
1586
|
// Advanced entity operations via tree.$ accessor
|
|
1587
1587
|
tree.$.users.addOne(newUser);
|
|
1588
|
-
tree.$.users.
|
|
1589
|
-
tree.$.users.updateMany([
|
|
1588
|
+
tree.$.users.where((u) => u.active); // Filtered signal
|
|
1589
|
+
tree.$.users.updateMany(['1'], { status: 'active' });
|
|
1590
1590
|
|
|
1591
1591
|
// Entity helpers work with nested structures
|
|
1592
1592
|
// Example: deeply nested entities in a domain-driven design pattern
|
|
@@ -1603,13 +1603,13 @@ const appTree = signalTree({
|
|
|
1603
1603
|
reports: entityMap<Report>(),
|
|
1604
1604
|
},
|
|
1605
1605
|
},
|
|
1606
|
-
})
|
|
1606
|
+
});
|
|
1607
1607
|
|
|
1608
1608
|
// Access nested entities using tree.$ accessor
|
|
1609
|
-
appTree.$.app.data.users.
|
|
1610
|
-
appTree.$.app.data.products.
|
|
1611
|
-
appTree.$.admin.data.logs.all; // All items as array
|
|
1612
|
-
appTree.$.admin.data.reports.
|
|
1609
|
+
appTree.$.app.data.users.where((u) => u.isAdmin); // Filtered signal
|
|
1610
|
+
appTree.$.app.data.products.count(); // Count
|
|
1611
|
+
appTree.$.admin.data.logs.all(); // All items as array
|
|
1612
|
+
appTree.$.admin.data.reports.ids(); // ID array
|
|
1613
1613
|
|
|
1614
1614
|
// For async operations, use manual async or async helpers
|
|
1615
1615
|
async function fetchUsers() {
|
|
@@ -1628,7 +1628,7 @@ async function fetchUsers() {
|
|
|
1628
1628
|
### Full-Featured Development Composition
|
|
1629
1629
|
|
|
1630
1630
|
```typescript
|
|
1631
|
-
import { signalTree, batching,
|
|
1631
|
+
import { signalTree, batching, serialization, withTimeTravel, devTools } from '@signaltree/core';
|
|
1632
1632
|
|
|
1633
1633
|
// Full development stack (example)
|
|
1634
1634
|
const tree = signalTree({
|
|
@@ -1639,7 +1639,6 @@ const tree = signalTree({
|
|
|
1639
1639
|
},
|
|
1640
1640
|
}).with(
|
|
1641
1641
|
batching(), // Performance
|
|
1642
|
-
entities(), // Data management
|
|
1643
1642
|
// withAsync removed — use async helpers for API integration
|
|
1644
1643
|
serialization({
|
|
1645
1644
|
// State persistence
|
|
@@ -1653,7 +1652,9 @@ const tree = signalTree({
|
|
|
1653
1652
|
devTools({
|
|
1654
1653
|
// Debug tools (dev only)
|
|
1655
1654
|
name: 'MyApp',
|
|
1656
|
-
|
|
1655
|
+
enableTimeTravel: true,
|
|
1656
|
+
includePaths: ['app.*', 'ui.*'],
|
|
1657
|
+
formatPath: (path) => path.replace(/\.(\d+)/g, '[$1]'),
|
|
1657
1658
|
})
|
|
1658
1659
|
);
|
|
1659
1660
|
|
|
@@ -1661,7 +1662,7 @@ const tree = signalTree({
|
|
|
1661
1662
|
async function fetchUser(id: string) {
|
|
1662
1663
|
return await api.getUser(id);
|
|
1663
1664
|
}
|
|
1664
|
-
tree.$.app.data.users.byId(userId)(); // O(1) lookup
|
|
1665
|
+
tree.$.app.data.users.byId(userId)?.(); // O(1) lookup
|
|
1665
1666
|
tree.undo(); // Time travel
|
|
1666
1667
|
tree.save(); // Persistence
|
|
1667
1668
|
```
|
|
@@ -1669,12 +1670,11 @@ tree.save(); // Persistence
|
|
|
1669
1670
|
### Production-Ready Composition
|
|
1670
1671
|
|
|
1671
1672
|
```typescript
|
|
1672
|
-
import { signalTree, batching,
|
|
1673
|
+
import { signalTree, batching, serialization } from '@signaltree/core';
|
|
1673
1674
|
|
|
1674
1675
|
// Production build (no dev tools)
|
|
1675
1676
|
const tree = signalTree(initialState).with(
|
|
1676
1677
|
batching(), // Performance optimization
|
|
1677
|
-
entities(), // Data management
|
|
1678
1678
|
// withAsync removed — use async helpers for API integration
|
|
1679
1679
|
serialization({
|
|
1680
1680
|
// User preferences
|
|
@@ -1690,14 +1690,13 @@ const tree = signalTree(initialState).with(
|
|
|
1690
1690
|
### Conditional Enhancement
|
|
1691
1691
|
|
|
1692
1692
|
```typescript
|
|
1693
|
-
import { signalTree, batching,
|
|
1693
|
+
import { signalTree, batching, devTools, withTimeTravel } from '@signaltree/core';
|
|
1694
1694
|
|
|
1695
1695
|
const isDevelopment = process.env['NODE_ENV'] === 'development';
|
|
1696
1696
|
|
|
1697
1697
|
// Conditional enhancement based on environment
|
|
1698
1698
|
const tree = signalTree(state).with(
|
|
1699
1699
|
batching(), // Always include performance
|
|
1700
|
-
entities(), // Always include data management
|
|
1701
1700
|
...(isDevelopment
|
|
1702
1701
|
? [
|
|
1703
1702
|
// Development-only features
|
|
@@ -1742,7 +1741,7 @@ const tree = signalTree(state);
|
|
|
1742
1741
|
const tree2 = tree.with(batching());
|
|
1743
1742
|
|
|
1744
1743
|
// Phase 3: Add data management for collections
|
|
1745
|
-
const tree3 = tree2
|
|
1744
|
+
const tree3 = tree2; // entityMap is auto-processed in v7
|
|
1746
1745
|
|
|
1747
1746
|
// Phase 4: Add async for API integration
|
|
1748
1747
|
// withAsync removed — no explicit async enhancer; use async helpers instead
|
|
@@ -1820,7 +1819,7 @@ For fair, reproducible measurements that reflect your app and hardware, use the
|
|
|
1820
1819
|
{{ userTree.$.error() }}
|
|
1821
1820
|
<button (click)="loadUsers()">Retry</button>
|
|
1822
1821
|
</div>
|
|
1823
|
-
} @else { @for (user of users
|
|
1822
|
+
} @else { @for (user of users(); track user.id) {
|
|
1824
1823
|
<div class="user-card">
|
|
1825
1824
|
<h3>{{ user.name }}</h3>
|
|
1826
1825
|
<p>{{ user.email }}</p>
|
|
@@ -1848,6 +1847,8 @@ class UserManagerComponent implements OnInit {
|
|
|
1848
1847
|
form: { id: '', name: '', email: '' },
|
|
1849
1848
|
});
|
|
1850
1849
|
|
|
1850
|
+
readonly users = this.userTree.$.users;
|
|
1851
|
+
|
|
1851
1852
|
constructor(private userService: UserService) {}
|
|
1852
1853
|
|
|
1853
1854
|
ngOnInit() {
|
|
@@ -2032,9 +2033,9 @@ tree.destroy(); // Cleanup resources
|
|
|
2032
2033
|
|
|
2033
2034
|
// Entity helpers (when using entityMap + entities)
|
|
2034
2035
|
// tree.$.users.addOne(user); // Add single entity
|
|
2035
|
-
// tree.$.users.byId(id)(); // O(1) lookup by ID
|
|
2036
|
-
// tree.$.users.all;
|
|
2037
|
-
// tree.$.users.
|
|
2036
|
+
// tree.$.users.byId(id)?.(); // O(1) lookup by ID
|
|
2037
|
+
// tree.$.users.all(); // Get all as array
|
|
2038
|
+
// tree.$.users.where(pred)(); // Filtered array
|
|
2038
2039
|
```
|
|
2039
2040
|
|
|
2040
2041
|
## Extending with enhancers
|
|
@@ -2054,8 +2055,8 @@ All enhancers are included in `@signaltree/core`:
|
|
|
2054
2055
|
|
|
2055
2056
|
- **batching()** - Batch multiple updates for better performance
|
|
2056
2057
|
- **memoization()** - Intelligent caching & performance optimization
|
|
2057
|
-
- **entities()** -
|
|
2058
|
-
- **devTools()** -
|
|
2058
|
+
- **entities()** - Legacy helper (not required when using `entityMap()` in v7)
|
|
2059
|
+
- **devTools()** - Auto-connect, path actions, and time-travel dispatch
|
|
2059
2060
|
- **withTimeTravel()** - Undo/redo functionality & state history
|
|
2060
2061
|
- **serialization()** - State persistence & SSR support
|
|
2061
2062
|
- **createDevTree()** - Pre-configured development setup
|
|
@@ -2210,11 +2211,11 @@ All enhancers are now consolidated in the core package. The following features a
|
|
|
2210
2211
|
|
|
2211
2212
|
### Advanced Features
|
|
2212
2213
|
|
|
2213
|
-
- **entities()** (+0.97KB gzipped) -
|
|
2214
|
+
- **entities()** (+0.97KB gzipped) - Legacy helper (not required when using `entityMap()` in v7)
|
|
2214
2215
|
|
|
2215
2216
|
### Development Tools
|
|
2216
2217
|
|
|
2217
|
-
- **devTools()** (+2.49KB gzipped) -
|
|
2218
|
+
- **devTools()** (+2.49KB gzipped) - Auto-connect, path actions, and time-travel dispatch
|
|
2218
2219
|
- **withTimeTravel()** (+1.75KB gzipped) - Undo/redo functionality & state history
|
|
2219
2220
|
|
|
2220
2221
|
### Integration & Convenience
|
|
@@ -216,7 +216,7 @@ function sanitizeState(value, options, depth = 0, seen = new WeakSet()) {
|
|
|
216
216
|
if (typeof value === 'number' || typeof value === 'boolean') return value;
|
|
217
217
|
if (typeof value === 'bigint') return `${value.toString()}n`;
|
|
218
218
|
if (typeof value === 'symbol') return String(value);
|
|
219
|
-
if (typeof value === 'function') return
|
|
219
|
+
if (typeof value === 'function') return undefined;
|
|
220
220
|
if (depth >= maxDepth) return '[MaxDepth]';
|
|
221
221
|
if (value instanceof Date) return value.toISOString();
|
|
222
222
|
if (value instanceof RegExp) return value.toString();
|
|
@@ -245,6 +245,7 @@ function sanitizeState(value, options, depth = 0, seen = new WeakSet()) {
|
|
|
245
245
|
}
|
|
246
246
|
const result = {};
|
|
247
247
|
for (const [key, val] of Object.entries(obj)) {
|
|
248
|
+
if (typeof val === 'function') continue;
|
|
248
249
|
result[key] = sanitizeState(val, options, depth + 1, seen);
|
|
249
250
|
}
|
|
250
251
|
return result;
|
|
@@ -299,6 +300,7 @@ function devTools(config = {}) {
|
|
|
299
300
|
logger.logComposition(modules, 'with');
|
|
300
301
|
};
|
|
301
302
|
const activeProfiles = new Map();
|
|
303
|
+
let browserDevToolsConnection = null;
|
|
302
304
|
let browserDevTools = null;
|
|
303
305
|
let isConnected = false;
|
|
304
306
|
let isApplyingExternalState = false;
|
|
@@ -374,7 +376,7 @@ function devTools(config = {}) {
|
|
|
374
376
|
lastSnapshot = currentSnapshot;
|
|
375
377
|
return;
|
|
376
378
|
}
|
|
377
|
-
const effectiveAction = pendingExplicitAction ? pendingAction : formattedPaths.length === 1 ? buildAction(`SignalTree/${formattedPaths[0]}`, formattedPaths[0]) : formattedPaths.length > 1 ? buildAction('SignalTree/
|
|
379
|
+
const effectiveAction = pendingExplicitAction ? pendingAction : formattedPaths.length === 1 ? buildAction(`SignalTree/${formattedPaths[0]}`, formattedPaths[0]) : formattedPaths.length > 1 ? buildAction('SignalTree/update', formattedPaths) : buildAction('SignalTree/update');
|
|
378
380
|
const actionMeta = {
|
|
379
381
|
timestamp: Date.now(),
|
|
380
382
|
...(pendingSource && {
|
|
@@ -521,15 +523,20 @@ function devTools(config = {}) {
|
|
|
521
523
|
skip: true
|
|
522
524
|
}
|
|
523
525
|
});
|
|
526
|
+
browserDevToolsConnection = connection;
|
|
524
527
|
browserDevTools = {
|
|
525
528
|
send: connection.send,
|
|
526
529
|
subscribe: connection.subscribe
|
|
527
530
|
};
|
|
528
531
|
if (browserDevTools.subscribe && !unsubscribeDevTools) {
|
|
529
|
-
browserDevTools.subscribe(handleDevToolsMessage);
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
}
|
|
532
|
+
const maybeUnsubscribe = browserDevTools.subscribe(handleDevToolsMessage);
|
|
533
|
+
if (typeof maybeUnsubscribe === 'function') {
|
|
534
|
+
unsubscribeDevTools = maybeUnsubscribe;
|
|
535
|
+
} else {
|
|
536
|
+
unsubscribeDevTools = () => {
|
|
537
|
+
browserDevTools?.subscribe?.(() => void 0);
|
|
538
|
+
};
|
|
539
|
+
}
|
|
533
540
|
}
|
|
534
541
|
const rawSnapshot = readSnapshot();
|
|
535
542
|
const sanitized = buildSerializedState(rawSnapshot);
|
|
@@ -652,7 +659,17 @@ function devTools(config = {}) {
|
|
|
652
659
|
initBrowserDevTools();
|
|
653
660
|
},
|
|
654
661
|
disconnectDevTools() {
|
|
662
|
+
try {
|
|
663
|
+
unsubscribeDevTools?.();
|
|
664
|
+
} catch {}
|
|
665
|
+
try {
|
|
666
|
+
browserDevToolsConnection?.unsubscribe?.();
|
|
667
|
+
} catch {}
|
|
668
|
+
try {
|
|
669
|
+
browserDevToolsConnection?.disconnect?.();
|
|
670
|
+
} catch {}
|
|
655
671
|
browserDevTools = null;
|
|
672
|
+
browserDevToolsConnection = null;
|
|
656
673
|
isConnected = false;
|
|
657
674
|
if (unsubscribeNotifier) {
|
|
658
675
|
unsubscribeNotifier();
|
|
@@ -662,10 +679,7 @@ function devTools(config = {}) {
|
|
|
662
679
|
unsubscribeFlush();
|
|
663
680
|
unsubscribeFlush = null;
|
|
664
681
|
}
|
|
665
|
-
|
|
666
|
-
unsubscribeDevTools();
|
|
667
|
-
unsubscribeDevTools = null;
|
|
668
|
-
}
|
|
682
|
+
unsubscribeDevTools = null;
|
|
669
683
|
if (effectRef) {
|
|
670
684
|
effectRef.destroy();
|
|
671
685
|
effectRef = null;
|
package/dist/lib/utils.js
CHANGED
|
@@ -180,6 +180,8 @@ function unwrap(node) {
|
|
|
180
180
|
} else {
|
|
181
181
|
result[key] = unwrappedValue;
|
|
182
182
|
}
|
|
183
|
+
} else if (typeof value === 'function') {
|
|
184
|
+
continue;
|
|
183
185
|
} else if (typeof value === 'object' && value !== null && !Array.isArray(value) && !isBuiltInObject(value)) {
|
|
184
186
|
result[key] = unwrap(value);
|
|
185
187
|
} else {
|
|
@@ -212,6 +214,9 @@ function unwrap(node) {
|
|
|
212
214
|
if (typeof v === 'function') continue;
|
|
213
215
|
}
|
|
214
216
|
const value = node[key];
|
|
217
|
+
if (typeof value === 'function' && !isNodeAccessor(value) && !isSignal(value)) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
215
220
|
if (isNodeAccessor(value)) {
|
|
216
221
|
const unwrappedValue = value();
|
|
217
222
|
if (typeof unwrappedValue === 'object' && unwrappedValue !== null && !Array.isArray(unwrappedValue) && !isBuiltInObject(unwrappedValue)) {
|
|
@@ -235,6 +240,9 @@ function unwrap(node) {
|
|
|
235
240
|
const symbols = Object.getOwnPropertySymbols(node);
|
|
236
241
|
for (const sym of symbols) {
|
|
237
242
|
const value = node[sym];
|
|
243
|
+
if (typeof value === 'function' && !isNodeAccessor(value) && !isSignal(value)) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
238
246
|
if (isNodeAccessor(value)) {
|
|
239
247
|
const unwrappedValue = value();
|
|
240
248
|
if (typeof unwrappedValue === 'object' && unwrappedValue !== null && !Array.isArray(unwrappedValue) && !isBuiltInObject(unwrappedValue)) {
|
|
@@ -263,6 +271,12 @@ function snapshotState(state) {
|
|
|
263
271
|
function applyState(stateNode, snapshot) {
|
|
264
272
|
if (snapshot === null || snapshot === undefined) return;
|
|
265
273
|
if (typeof snapshot !== 'object') return;
|
|
274
|
+
if (stateNode && typeof stateNode === 'object' && typeof stateNode.setAll === 'function' && snapshot && typeof snapshot === 'object' && Array.isArray(snapshot.all)) {
|
|
275
|
+
try {
|
|
276
|
+
stateNode.setAll(snapshot.all);
|
|
277
|
+
return;
|
|
278
|
+
} catch {}
|
|
279
|
+
}
|
|
266
280
|
for (const key of Object.keys(snapshot)) {
|
|
267
281
|
const val = snapshot[key];
|
|
268
282
|
const target = stateNode[key];
|
|
@@ -288,6 +302,14 @@ function applyState(stateNode, snapshot) {
|
|
|
288
302
|
target(val);
|
|
289
303
|
} catch {}
|
|
290
304
|
}
|
|
305
|
+
} else if (target && typeof target === 'object' && val && typeof val === 'object' && !Array.isArray(target) && !Array.isArray(val)) {
|
|
306
|
+
try {
|
|
307
|
+
applyState(target, val);
|
|
308
|
+
} catch {
|
|
309
|
+
try {
|
|
310
|
+
stateNode[key] = val;
|
|
311
|
+
} catch {}
|
|
312
|
+
}
|
|
291
313
|
} else {
|
|
292
314
|
try {
|
|
293
315
|
stateNode[key] = val;
|