mutts 1.0.9 → 1.0.11
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 +60 -35
- package/dist/browser.cjs +1333 -1760
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +2 -1392
- package/dist/browser.dev.cjs +114 -0
- package/dist/browser.dev.cjs.map +1 -0
- package/dist/browser.dev.d.ts +2 -0
- package/dist/browser.dev.esm.js +5 -0
- package/dist/browser.dev.esm.js.map +1 -0
- package/dist/browser.esm.js +18 -97
- package/dist/browser.esm.js.map +1 -1
- package/dist/chunks/{async-browser-lvzLOCgk.cjs → async-browser-Dgr5CreQ.cjs} +16 -95
- package/dist/chunks/async-browser-Dgr5CreQ.cjs.map +1 -0
- package/dist/chunks/{async-node-C3DeIb0y.cjs → async-node-3PrbVAbB.cjs} +3 -1
- package/dist/chunks/async-node-3PrbVAbB.cjs.map +1 -0
- package/dist/chunks/index-Sf74wXTV.esm.js +2577 -0
- package/dist/chunks/index-Sf74wXTV.esm.js.map +1 -0
- package/dist/chunks/node-Bo7WU5S2.esm.js +96 -0
- package/dist/chunks/node-Bo7WU5S2.esm.js.map +1 -0
- package/dist/chunks/{index-VTO-b2vR.cjs → proxy-Cc79Lrzj.cjs} +2593 -3216
- package/dist/chunks/proxy-Cc79Lrzj.cjs.map +1 -0
- package/dist/chunks/{index-CtA2AWl3.esm.js → proxy-D2C49sXH.esm.js} +2570 -3174
- package/dist/chunks/proxy-D2C49sXH.esm.js.map +1 -0
- package/dist/debug.cjs +987 -28
- package/dist/debug.cjs.map +1 -1
- package/dist/debug.d.ts +37 -38
- package/dist/debug.esm.js +963 -1
- package/dist/debug.esm.js.map +1 -1
- package/dist/devtools/manifest.json +1 -1
- package/dist/devtools/panel.html +1 -1
- package/dist/devtools/panel.js +107 -94
- package/dist/devtools/panel.js.map +1 -1
- package/dist/index.d.ts +1322 -1
- package/dist/mutts.umd.js +6647 -1
- package/dist/mutts.umd.js.map +1 -1
- package/dist/mutts.umd.min.js +1 -1
- package/dist/mutts.umd.min.js.map +1 -1
- package/dist/node.cjs +69 -63
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.dev.cjs +114 -0
- package/dist/node.dev.cjs.map +1 -0
- package/dist/node.dev.d.ts +2 -0
- package/dist/node.dev.esm.js +6 -0
- package/dist/node.dev.esm.js.map +1 -0
- package/dist/node.esm.js +4 -97
- package/dist/node.esm.js.map +1 -1
- package/dist/{types-DaHFfhlN.d.ts → types-Bx2PhORg.d.ts} +134 -88
- package/docs/ai/api-reference.md +11 -32
- package/docs/ai/manual.md +297 -239
- package/docs/reactive/advanced.md +318 -9
- package/docs/reactive/attend.md +2 -4
- package/docs/reactive/collections.md +22 -187
- package/docs/reactive/core.md +223 -131
- package/docs/reactive/debugging.md +119 -12
- package/docs/reactive/error-handling.md +10 -10
- package/docs/reactive/resource.md +125 -0
- package/docs/reactive.md +3 -4
- package/docs/utils.md +70 -0
- package/docs/zone.md +1 -1
- package/package.json +76 -38
- package/dist/chunks/async-browser-lvzLOCgk.cjs.map +0 -1
- package/dist/chunks/async-node-C3DeIb0y.cjs.map +0 -1
- package/dist/chunks/index-2vea86wD.esm.js +0 -3011
- package/dist/chunks/index-2vea86wD.esm.js.map +0 -1
- package/dist/chunks/index-CtA2AWl3.esm.js.map +0 -1
- package/dist/chunks/index-VTO-b2vR.cjs.map +0 -1
- package/dist/debug/debug.d.ts +0 -122
- package/dist/debug/debug.d.ts.map +0 -1
- package/dist/debug/index.d.ts +0 -4
- package/dist/debug/index.d.ts.map +0 -1
- package/dist/debug/lineage-panel.d.ts +0 -5
- package/dist/debug/lineage-panel.d.ts.map +0 -1
- package/dist/debug/lineage.d.ts +0 -79
- package/dist/debug/lineage.d.ts.map +0 -1
- package/dist/src/async/browser.d.ts +0 -2
- package/dist/src/async/browser.d.ts.map +0 -1
- package/dist/src/async/index.d.ts +0 -19
- package/dist/src/async/index.d.ts.map +0 -1
- package/dist/src/async/node.d.ts +0 -2
- package/dist/src/async/node.d.ts.map +0 -1
- package/dist/src/decorator.d.ts +0 -106
- package/dist/src/decorator.d.ts.map +0 -1
- package/dist/src/destroyable.d.ts +0 -87
- package/dist/src/destroyable.d.ts.map +0 -1
- package/dist/src/entry-browser.d.ts +0 -3
- package/dist/src/entry-browser.d.ts.map +0 -1
- package/dist/src/entry-node.d.ts +0 -3
- package/dist/src/entry-node.d.ts.map +0 -1
- package/dist/src/eventful.d.ts +0 -20
- package/dist/src/eventful.d.ts.map +0 -1
- package/dist/src/flavored.d.ts +0 -33
- package/dist/src/flavored.d.ts.map +0 -1
- package/dist/src/index.d.ts +0 -14
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/indexable.d.ts +0 -243
- package/dist/src/indexable.d.ts.map +0 -1
- package/dist/src/introspection.d.ts +0 -27
- package/dist/src/introspection.d.ts.map +0 -1
- package/dist/src/iterableWeak.d.ts +0 -53
- package/dist/src/iterableWeak.d.ts.map +0 -1
- package/dist/src/mixins.d.ts +0 -25
- package/dist/src/mixins.d.ts.map +0 -1
- package/dist/src/promiseChain.d.ts +0 -20
- package/dist/src/promiseChain.d.ts.map +0 -1
- package/dist/src/reactive/array.d.ts +0 -48
- package/dist/src/reactive/array.d.ts.map +0 -1
- package/dist/src/reactive/buffer.d.ts +0 -120
- package/dist/src/reactive/buffer.d.ts.map +0 -1
- package/dist/src/reactive/change.d.ts +0 -29
- package/dist/src/reactive/change.d.ts.map +0 -1
- package/dist/src/reactive/deep-touch.d.ts +0 -28
- package/dist/src/reactive/deep-touch.d.ts.map +0 -1
- package/dist/src/reactive/deep-watch-state.d.ts +0 -25
- package/dist/src/reactive/deep-watch-state.d.ts.map +0 -1
- package/dist/src/reactive/deep-watch.d.ts +0 -20
- package/dist/src/reactive/deep-watch.d.ts.map +0 -1
- package/dist/src/reactive/describe.d.ts +0 -12
- package/dist/src/reactive/describe.d.ts.map +0 -1
- package/dist/src/reactive/effect-context.d.ts +0 -34
- package/dist/src/reactive/effect-context.d.ts.map +0 -1
- package/dist/src/reactive/effects.d.ts +0 -164
- package/dist/src/reactive/effects.d.ts.map +0 -1
- package/dist/src/reactive/index.d.ts +0 -19
- package/dist/src/reactive/index.d.ts.map +0 -1
- package/dist/src/reactive/map.d.ts +0 -28
- package/dist/src/reactive/map.d.ts.map +0 -1
- package/dist/src/reactive/memoize.d.ts +0 -28
- package/dist/src/reactive/memoize.d.ts.map +0 -1
- package/dist/src/reactive/non-reactive-state.d.ts +0 -9
- package/dist/src/reactive/non-reactive-state.d.ts.map +0 -1
- package/dist/src/reactive/non-reactive.d.ts +0 -11
- package/dist/src/reactive/non-reactive.d.ts.map +0 -1
- package/dist/src/reactive/project.d.ts +0 -40
- package/dist/src/reactive/project.d.ts.map +0 -1
- package/dist/src/reactive/proxy-state.d.ts +0 -8
- package/dist/src/reactive/proxy-state.d.ts.map +0 -1
- package/dist/src/reactive/proxy.d.ts +0 -23
- package/dist/src/reactive/proxy.d.ts.map +0 -1
- package/dist/src/reactive/record.d.ts +0 -115
- package/dist/src/reactive/record.d.ts.map +0 -1
- package/dist/src/reactive/register.d.ts +0 -125
- package/dist/src/reactive/register.d.ts.map +0 -1
- package/dist/src/reactive/registry.d.ts +0 -21
- package/dist/src/reactive/registry.d.ts.map +0 -1
- package/dist/src/reactive/set.d.ts +0 -26
- package/dist/src/reactive/set.d.ts.map +0 -1
- package/dist/src/reactive/tracking.d.ts +0 -7
- package/dist/src/reactive/tracking.d.ts.map +0 -1
- package/dist/src/reactive/types.d.ts +0 -424
- package/dist/src/reactive/types.d.ts.map +0 -1
- package/dist/src/reactive/watch.d.ts +0 -48
- package/dist/src/reactive/watch.d.ts.map +0 -1
- package/dist/src/std-decorators.d.ts +0 -45
- package/dist/src/std-decorators.d.ts.map +0 -1
- package/dist/src/utils.d.ts +0 -49
- package/dist/src/utils.d.ts.map +0 -1
- package/dist/src/zone.d.ts +0 -40
- package/dist/src/zone.d.ts.map +0 -1
- package/docs/reactive/describe.md +0 -85
- package/docs/reactive/project.md +0 -93
- package/docs/reactive/scan.md +0 -293
- package/src/async/browser.ts +0 -323
- package/src/async/index.ts +0 -27
- package/src/async/node.ts +0 -92
- package/src/decorator.ts +0 -272
- package/src/destroyable.ts +0 -199
- package/src/entry-browser.ts +0 -5
- package/src/entry-node.ts +0 -5
- package/src/eventful.ts +0 -110
- package/src/flavored.ts +0 -106
- package/src/index.d.ts +0 -12
- package/src/index.ts +0 -64
- package/src/indexable.ts +0 -526
- package/src/introspection.ts +0 -59
- package/src/iterableWeak.ts +0 -233
- package/src/mixins.ts +0 -123
- package/src/promiseChain.ts +0 -110
- package/src/reactive/array.ts +0 -500
- package/src/reactive/buffer.ts +0 -328
- package/src/reactive/change.ts +0 -131
- package/src/reactive/deep-touch.ts +0 -273
- package/src/reactive/deep-watch-state.ts +0 -82
- package/src/reactive/deep-watch.ts +0 -171
- package/src/reactive/describe.ts +0 -39
- package/src/reactive/effect-context.ts +0 -83
- package/src/reactive/effects.ts +0 -1434
- package/src/reactive/index.ts +0 -72
- package/src/reactive/map.ts +0 -142
- package/src/reactive/memoize.ts +0 -186
- package/src/reactive/non-reactive-state.ts +0 -49
- package/src/reactive/non-reactive.ts +0 -43
- package/src/reactive/project.md +0 -107
- package/src/reactive/project.ts +0 -430
- package/src/reactive/proxy-state.ts +0 -27
- package/src/reactive/proxy.ts +0 -282
- package/src/reactive/record.ts +0 -181
- package/src/reactive/register.ts +0 -538
- package/src/reactive/registry.ts +0 -72
- package/src/reactive/set.ts +0 -117
- package/src/reactive/tracking.ts +0 -41
- package/src/reactive/types.ts +0 -520
- package/src/reactive/watch.ts +0 -180
- package/src/std-decorators.ts +0 -256
- package/src/utils.ts +0 -300
- package/src/zone.ts +0 -142
package/docs/reactive/core.md
CHANGED
|
@@ -4,27 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
- [Introduction](#introduction)
|
|
6
6
|
- [Getting Started](#getting-started)
|
|
7
|
-
|
|
7
|
+
- [5-Minute Quick Start](#5-minute-quick-start)
|
|
8
8
|
- [Core API](#core-api)
|
|
9
9
|
- [Effect System](#effect-system)
|
|
10
|
-
|
|
10
|
+
- [Error Handling](./error-handling.md)
|
|
11
11
|
- [Atomic Operations](./advanced.md#atomic-operations)
|
|
12
12
|
- [Advanced Effects](./advanced.md#advanced-effects)
|
|
13
13
|
- [Evolution Tracking](./advanced.md#evolution-tracking)
|
|
14
14
|
- [Prototype Chains and Pure Objects](./advanced.md#prototype-chains-and-pure-objects)
|
|
15
15
|
- [Recursive Touching](./advanced.md#recursive-touching)
|
|
16
|
-
|
|
17
|
-
- [
|
|
18
|
-
|
|
19
|
-
- [
|
|
20
|
-
- [Non-Reactive System](./advanced.md#non-reactive-system)
|
|
21
|
-
- [Array Mapping](./advanced.md#array-mapping)
|
|
22
|
-
- [Projection](./advanced.md#projection)
|
|
23
|
-
- [Reactive Property Description](./describe.md)
|
|
24
|
-
- [Record Organization](./advanced.md#record-organization)
|
|
16
|
+
- [Collections](./collections.md)
|
|
17
|
+
- [Class Reactivity](#class-reactivity)
|
|
18
|
+
- [Non-Reactive System](#non-reactive-system)
|
|
19
|
+
- [Morphing](./advanced.md#morph)
|
|
25
20
|
- [Memoization](./advanced.md#memoization)
|
|
26
21
|
- [Debugging and Development](./advanced.md#debugging-and-development)
|
|
27
|
-
|
|
22
|
+
- [Cycle Detection](./advanced.md#cycle-detection)
|
|
28
23
|
|
|
29
24
|
## Introduction
|
|
30
25
|
|
|
@@ -44,10 +39,13 @@ Reactivity is a programming paradigm where the system automatically tracks depen
|
|
|
44
39
|
### Basic Example
|
|
45
40
|
|
|
46
41
|
```typescript
|
|
47
|
-
import { reactive, effect } from 'mutts
|
|
42
|
+
import { reactive, effect } from 'mutts'
|
|
48
43
|
|
|
49
44
|
// Create a reactive object
|
|
50
|
-
const user = reactive({
|
|
45
|
+
const user = reactive({
|
|
46
|
+
name: "John",
|
|
47
|
+
age: 30
|
|
48
|
+
})
|
|
51
49
|
|
|
52
50
|
// Create an effect that depends on user properties
|
|
53
51
|
effect(() => {
|
|
@@ -55,8 +53,8 @@ effect(() => {
|
|
|
55
53
|
})
|
|
56
54
|
|
|
57
55
|
// When properties change, the effect automatically re-runs
|
|
58
|
-
user.name = "Jane"
|
|
59
|
-
user.age = 25
|
|
56
|
+
user.name = "Jane" // Triggers effect
|
|
57
|
+
user.age = 25 // Triggers effect
|
|
60
58
|
```
|
|
61
59
|
|
|
62
60
|
## Getting Started
|
|
@@ -66,38 +64,45 @@ user.age = 25 // Triggers effect
|
|
|
66
64
|
Learn the essentials in 5 minutes:
|
|
67
65
|
|
|
68
66
|
**1. Make state reactive:**
|
|
67
|
+
|
|
69
68
|
```typescript
|
|
70
|
-
import { reactive, effect } from 'mutts
|
|
69
|
+
import { reactive, effect } from 'mutts'
|
|
71
70
|
|
|
72
|
-
const state = reactive({
|
|
71
|
+
const state = reactive({
|
|
72
|
+
count: 0,
|
|
73
|
+
name: "John"
|
|
74
|
+
})
|
|
73
75
|
```
|
|
74
76
|
|
|
75
77
|
**2. React to changes:**
|
|
78
|
+
|
|
76
79
|
```typescript
|
|
77
80
|
// Effects automatically re-run when dependencies change
|
|
78
81
|
effect(() => {
|
|
79
|
-
|
|
82
|
+
console.log(`Hello ${state.name}, count is ${state.count}`)
|
|
80
83
|
})
|
|
81
84
|
|
|
82
|
-
state.count++
|
|
83
|
-
state.name = "Jane"
|
|
85
|
+
state.count++ // Triggers effect
|
|
86
|
+
state.name = "Jane" // Triggers effect
|
|
84
87
|
```
|
|
85
88
|
|
|
86
89
|
**3. Work with arrays:**
|
|
90
|
+
|
|
87
91
|
```typescript
|
|
88
92
|
const items = reactive([1, 2, 3])
|
|
89
93
|
|
|
90
94
|
effect(() => {
|
|
91
|
-
|
|
95
|
+
console.log(`Array length: ${items.length}`)
|
|
92
96
|
})
|
|
93
97
|
|
|
94
|
-
items.push(4)
|
|
98
|
+
items.push(4) // Triggers effect
|
|
95
99
|
```
|
|
96
100
|
|
|
97
101
|
**4. Use memoization:**
|
|
102
|
+
|
|
98
103
|
```typescript
|
|
99
104
|
const memoized = memoize((user: User) => {
|
|
100
|
-
|
|
105
|
+
return expensiveComputation(user)
|
|
101
106
|
})
|
|
102
107
|
|
|
103
108
|
// Only recomputes when user changes
|
|
@@ -105,25 +110,12 @@ const result = memoized(user)
|
|
|
105
110
|
```
|
|
106
111
|
|
|
107
112
|
**5. Map over arrays:**
|
|
108
|
-
```typescript
|
|
109
|
-
const source = reactive([1, 2, 3])
|
|
110
|
-
const doubled = project(source, ({ value }) => value * 2)
|
|
111
|
-
// [2, 4, 6]
|
|
112
113
|
|
|
113
|
-
source.push(4) // doubled automatically becomes [2, 4, 6, 8]
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
**6. Define properties reactively:**
|
|
117
114
|
```typescript
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
foo: { value: 1, enumerable: true }
|
|
121
|
-
})
|
|
122
|
-
describe(descriptors, target)
|
|
123
|
-
// target.foo is 1
|
|
115
|
+
const source = reactive([1, 2, 3])
|
|
116
|
+
const doubled = morph(source, value => value * 2) // [2, 4, 6]
|
|
124
117
|
|
|
125
|
-
|
|
126
|
-
// target.bar is 2 automatically
|
|
118
|
+
source.push(4) // doubled automatically becomes [2, 4, 6, 8]
|
|
127
119
|
```
|
|
128
120
|
|
|
129
121
|
**Ready to go!** Continue reading for advanced features.
|
|
@@ -139,7 +131,7 @@ npm install mutts
|
|
|
139
131
|
### Basic Usage
|
|
140
132
|
|
|
141
133
|
```typescript
|
|
142
|
-
import { reactive, effect } from 'mutts
|
|
134
|
+
import { reactive, effect } from 'mutts'
|
|
143
135
|
|
|
144
136
|
// Make an object reactive
|
|
145
137
|
const state = reactive({
|
|
@@ -157,17 +149,19 @@ effect(() => {
|
|
|
157
149
|
})
|
|
158
150
|
|
|
159
151
|
// Changes trigger effects automatically
|
|
160
|
-
state.count++
|
|
152
|
+
state.count++ // Triggers first effect
|
|
161
153
|
state.message = "Hi" // Triggers second effect
|
|
162
154
|
```
|
|
163
155
|
|
|
164
156
|
### Hello World Example
|
|
165
157
|
|
|
166
158
|
```typescript
|
|
167
|
-
import { reactive, effect } from 'mutts
|
|
159
|
+
import { reactive, effect } from 'mutts'
|
|
168
160
|
|
|
169
161
|
// Simple counter
|
|
170
|
-
const counter = reactive({
|
|
162
|
+
const counter = reactive({
|
|
163
|
+
value: 0
|
|
164
|
+
})
|
|
171
165
|
|
|
172
166
|
effect(() => {
|
|
173
167
|
document.body.innerHTML = `Count: ${counter.value}`
|
|
@@ -195,11 +189,14 @@ function reactive<T extends Record<PropertyKey, any>>(target: T): T
|
|
|
195
189
|
**Returns:** A reactive proxy of the original object
|
|
196
190
|
|
|
197
191
|
**Example:**
|
|
192
|
+
|
|
198
193
|
```typescript
|
|
199
|
-
const obj = {
|
|
200
|
-
|
|
194
|
+
const obj = {
|
|
195
|
+
count: 0
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const reactiveObj = reactive(obj) // reactiveObj is now reactive
|
|
201
199
|
|
|
202
|
-
// reactiveObj is now reactive
|
|
203
200
|
effect(() => {
|
|
204
201
|
console.log(reactiveObj.count) // Tracks dependency
|
|
205
202
|
})
|
|
@@ -209,28 +206,13 @@ reactiveObj.count = 5 // Triggers effect
|
|
|
209
206
|
|
|
210
207
|
**Note:** The same object will always return the same proxy instance.
|
|
211
208
|
|
|
212
|
-
**Pure Objects and Prototypes:**
|
|
213
|
-
|
|
214
|
-
`reactive()` works with any object type, including:
|
|
209
|
+
**Pure Objects and Prototypes:** `reactive()` works with any object type, including:
|
|
215
210
|
- Normal objects: `reactive({ x: 1 })`
|
|
216
211
|
- Pure objects: `reactive(Object.create(null))`
|
|
217
212
|
- Objects with prototypes: `reactive(Object.create(parent))`
|
|
218
213
|
- Class instances: `reactive(new MyClass())`
|
|
219
214
|
|
|
220
|
-
See [Prototype Chains and Pure Objects](#prototype-chains-and-pure-objects) for detailed information about prototype chain handling.
|
|
221
|
-
|
|
222
|
-
### `describe()`
|
|
223
|
-
|
|
224
|
-
Reactively defines properties on a target object based on a descriptors record.
|
|
225
|
-
|
|
226
|
-
```typescript
|
|
227
|
-
function describe<T extends object>(
|
|
228
|
-
descriptors: Record<PropertyKey, PropertyDescriptor>,
|
|
229
|
-
target: T = Object.create(null) as T
|
|
230
|
-
): T
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
See the [Reactive Property Description](./describe.md) guide for details.
|
|
215
|
+
See [Prototype Chains and Pure Objects](./advanced.md#prototype-chains-and-pure-objects) for detailed information about prototype chain handling.
|
|
234
216
|
|
|
235
217
|
### `effect()`
|
|
236
218
|
|
|
@@ -238,9 +220,9 @@ Creates a reactive effect that automatically re-runs when dependencies change.
|
|
|
238
220
|
|
|
239
221
|
```typescript
|
|
240
222
|
interface DependencyAccess {
|
|
241
|
-
tracked: DependencyFunction
|
|
242
|
-
ascend: DependencyFunction
|
|
243
|
-
reaction: boolean
|
|
223
|
+
tracked: DependencyFunction // Track dependencies in the current effect
|
|
224
|
+
ascend: DependencyFunction // Track dependencies in the parent effect
|
|
225
|
+
reaction: boolean // true after the first run
|
|
244
226
|
}
|
|
245
227
|
|
|
246
228
|
function effect(
|
|
@@ -261,16 +243,21 @@ function effect(
|
|
|
261
243
|
**Returns:** A cleanup function to stop the effect
|
|
262
244
|
|
|
263
245
|
**Example:**
|
|
246
|
+
|
|
264
247
|
```typescript
|
|
265
|
-
const state = reactive({
|
|
248
|
+
const state = reactive({
|
|
249
|
+
count: 0,
|
|
250
|
+
mood: 'happy'
|
|
251
|
+
})
|
|
266
252
|
|
|
267
253
|
const cleanup = effect(() => {
|
|
268
254
|
console.log(`Count is: ${state.count}`)
|
|
255
|
+
|
|
269
256
|
// Optional cleanup called before next run
|
|
270
257
|
return () => console.log('Cleaning up...')
|
|
271
258
|
})
|
|
272
259
|
|
|
273
|
-
state.count++
|
|
260
|
+
state.count++ // Does trigger: 1- the cleaning, 2- the effect
|
|
274
261
|
state.mood = 'surprised' // Does not trigger the effect
|
|
275
262
|
|
|
276
263
|
// Later...
|
|
@@ -290,8 +277,13 @@ effect(({ reaction }) => {
|
|
|
290
277
|
```
|
|
291
278
|
|
|
292
279
|
**Using effect with arguments (useful in loops):**
|
|
280
|
+
|
|
293
281
|
```typescript
|
|
294
|
-
const items = reactive([
|
|
282
|
+
const items = reactive([
|
|
283
|
+
{ id: 1 },
|
|
284
|
+
{ id: 2 },
|
|
285
|
+
{ id: 3 }
|
|
286
|
+
])
|
|
295
287
|
|
|
296
288
|
// Create effects in a loop, passing loop variables
|
|
297
289
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -310,8 +302,12 @@ function unwrap<T>(proxy: T): T
|
|
|
310
302
|
```
|
|
311
303
|
|
|
312
304
|
**Example:**
|
|
305
|
+
|
|
313
306
|
```typescript
|
|
314
|
-
const original = {
|
|
307
|
+
const original = {
|
|
308
|
+
count: 0
|
|
309
|
+
}
|
|
310
|
+
|
|
315
311
|
const reactive = reactive(original)
|
|
316
312
|
const unwrapped = unwrap(reactive)
|
|
317
313
|
|
|
@@ -342,7 +338,10 @@ function isNonReactive(obj: any): boolean
|
|
|
342
338
|
Effects are the core of the reactive system. They automatically track dependencies and re-run when those dependencies change.
|
|
343
339
|
|
|
344
340
|
```typescript
|
|
345
|
-
const state = reactive({
|
|
341
|
+
const state = reactive({
|
|
342
|
+
count: 0,
|
|
343
|
+
name: "John"
|
|
344
|
+
})
|
|
346
345
|
|
|
347
346
|
effect(() => {
|
|
348
347
|
// This effect depends on state.count
|
|
@@ -350,7 +349,7 @@ effect(() => {
|
|
|
350
349
|
})
|
|
351
350
|
|
|
352
351
|
// Only changing count triggers the effect
|
|
353
|
-
state.count = 5
|
|
352
|
+
state.count = 5 // Triggers effect
|
|
354
353
|
state.name = "Jane" // Does NOT trigger effect
|
|
355
354
|
```
|
|
356
355
|
|
|
@@ -359,7 +358,9 @@ state.name = "Jane" // Does NOT trigger effect
|
|
|
359
358
|
Effects return cleanup functions that you can call to stop tracking dependencies.
|
|
360
359
|
|
|
361
360
|
```typescript
|
|
362
|
-
const state = reactive({
|
|
361
|
+
const state = reactive({
|
|
362
|
+
count: 0
|
|
363
|
+
})
|
|
363
364
|
|
|
364
365
|
const stopEffect = effect(() => {
|
|
365
366
|
console.log(`Count: ${state.count}`)
|
|
@@ -379,13 +380,17 @@ The reactive system provides **automatic cleanup** for effects, making memory ma
|
|
|
379
380
|
|
|
380
381
|
1. **Parent-Child Cleanup**: When an effect is created inside another effect, it becomes a "child" of the parent effect. When the parent effect is cleaned up, it also cleans up its child effects.
|
|
381
382
|
|
|
382
|
-
2. **Garbage Collection Cleanup**: For top-level effects (not created inside other effects), the system uses JavaScript's garbage collection to automatically clean them up when their cleanup function no longer referenced.
|
|
383
|
+
2. **Garbage Collection Cleanup**: For top-level effects (not created inside other effects), the system uses JavaScript's garbage collection to automatically clean them up when their cleanup function is no longer referenced.
|
|
383
384
|
|
|
384
385
|
#### Examples
|
|
385
386
|
|
|
386
387
|
**Parent-Child Cleanup:**
|
|
388
|
+
|
|
387
389
|
```typescript
|
|
388
|
-
const state = reactive({
|
|
390
|
+
const state = reactive({
|
|
391
|
+
a: 1,
|
|
392
|
+
b: 2
|
|
393
|
+
})
|
|
389
394
|
|
|
390
395
|
const stopParent = effect(() => {
|
|
391
396
|
state.a
|
|
@@ -404,8 +409,11 @@ stopParent() // Logs (order may vary): "Child cleanup", then "Parent cleanup"
|
|
|
404
409
|
```
|
|
405
410
|
|
|
406
411
|
**Garbage Collection Cleanup:**
|
|
412
|
+
|
|
407
413
|
```typescript
|
|
408
|
-
const state = reactive({
|
|
414
|
+
const state = reactive({
|
|
415
|
+
value: 1
|
|
416
|
+
})
|
|
409
417
|
|
|
410
418
|
// Top-level effect - automatically cleaned up via garbage collection
|
|
411
419
|
effect(() => {
|
|
@@ -421,7 +429,10 @@ effect(() => {
|
|
|
421
429
|
While cleanup can be automatic via GC, you should **store and remember** cleanup functions both to prevent the effect from being garbage-collected (keeping it alive) and to perform immediate cleanup when needed. If you don't hold a reference to the cleanup (or to the effect), the effect can be collected and its cleanup called automatically by GC; storing a reference keeps it active under your control:
|
|
422
430
|
|
|
423
431
|
```typescript
|
|
424
|
-
const state = reactive({
|
|
432
|
+
const state = reactive({
|
|
433
|
+
value: 1
|
|
434
|
+
})
|
|
435
|
+
|
|
425
436
|
const activeEffects: (() => void)[] = []
|
|
426
437
|
|
|
427
438
|
// Store cleanup functions for effects with side effects
|
|
@@ -447,19 +458,79 @@ activeEffects.forEach(stop => stop())
|
|
|
447
458
|
```
|
|
448
459
|
|
|
449
460
|
**Key Points:**
|
|
450
|
-
|
|
451
461
|
- **You do not have to call cleanup** - GC may clean up effects when no references remain
|
|
452
462
|
- **You may want to call cleanup** - especially for effects with side effects
|
|
453
463
|
- **Store cleanup references to keep effects alive** - holding a reference prevents GC cleanup and gives you explicit stop control
|
|
454
464
|
- **Parent cleanup cleans child effects** - stopping a parent also stops its child effects
|
|
455
465
|
- **Child effects are referenced by parent effects** - and therefore are not subject to GC cleanups
|
|
456
466
|
|
|
467
|
+
### Object Lifecycle with `link` / `unlink`
|
|
468
|
+
|
|
469
|
+
While effects handle their own cleanup automatically, **derived reactive objects** (morph results, lifted arrays, processed children, etc.) often need explicit lifecycle management. `link` and `unlink` solve this by forming a **cleanup tree** — an ownership graph independent of the effect hierarchy.
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
import { link, unlink } from 'mutts'
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
#### Attaching cleanup dependencies
|
|
476
|
+
|
|
477
|
+
`link(owner, ...deps)` attaches dependencies to an owner object. Dependencies can be:
|
|
478
|
+
- **Functions** — called with an optional `CleanupReason` on disposal
|
|
479
|
+
- **Objects** — recursively `unlink`ed on disposal
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
const parent = reactive({
|
|
483
|
+
items: []
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
// Attach a cleanup callback
|
|
487
|
+
link(parent, () => console.log('parent disposed'))
|
|
488
|
+
|
|
489
|
+
// Attach child objects — they will be recursively unlinked
|
|
490
|
+
const childA = morph(parent.items, ({ value }) => value * 2)
|
|
491
|
+
const childB = morph(parent.items, ({ value }) => value + 1)
|
|
492
|
+
|
|
493
|
+
link(parent, childA, childB)
|
|
494
|
+
|
|
495
|
+
// Mixed: objects and functions together
|
|
496
|
+
link(parent, childA, () => timer.clear())
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
#### Disposing an object
|
|
500
|
+
|
|
501
|
+
`unlink(obj)` disposes all dependencies attached to `obj`:
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
unlink(parent)
|
|
505
|
+
|
|
506
|
+
// 1. childA is recursively unlinked (its own deps disposed)
|
|
507
|
+
// 2. childB is recursively unlinked
|
|
508
|
+
// 3. the callback runs: "parent disposed"
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
Calling `unlink` twice is safe — the second call is a no-op. Not calling `unlink` is also fine — the object will be cleaned up when garbage is collected.
|
|
512
|
+
|
|
513
|
+
#### When to use `link`/`unlink` vs effects
|
|
514
|
+
|
|
515
|
+
| Scenario | Use |
|
|
516
|
+
|---|---|
|
|
517
|
+
| Reacting to state changes | `effect` |
|
|
518
|
+
| Tying a derived object's lifetime to its owner | `link` / `unlink` |
|
|
519
|
+
| Cleaning up timers, listeners, DOM nodes | `effect` return or `link` callback |
|
|
520
|
+
| Building a cleanup tree across multiple reactive objects | `link` / `unlink` |
|
|
521
|
+
|
|
522
|
+
**Rule of thumb**: if the thing you're cleaning up is a *reactive object* (not a function), prefer `link`. If it's a *side effect* (timer, listener), prefer an `effect` return or a `link` callback.
|
|
523
|
+
|
|
457
524
|
### Effect Dependencies
|
|
458
525
|
|
|
459
526
|
Effects automatically track which reactive properties they access.
|
|
460
527
|
|
|
461
528
|
```typescript
|
|
462
|
-
const state = reactive({
|
|
529
|
+
const state = reactive({
|
|
530
|
+
a: 1,
|
|
531
|
+
b: 2,
|
|
532
|
+
c: 3
|
|
533
|
+
})
|
|
463
534
|
|
|
464
535
|
effect(() => {
|
|
465
536
|
// Only tracks state.a and state.b
|
|
@@ -474,13 +545,11 @@ state.c = 15 // Does NOT trigger effect
|
|
|
474
545
|
|
|
475
546
|
### Async Effects and the `access` Parameter
|
|
476
547
|
|
|
477
|
-
The `effect` function provides a special `access` parameter with `tracked` and `ascend` functions that restore the active effect context for dependency tracking in asynchronous operations.
|
|
478
|
-
|
|
479
|
-
In modern `mutts`, this is powered by the **Zone system**. When zones are registered in `asyncZone`, the active effect context is automatically preserved across `await` points and timers, making manual use of `tracked` optional for these cases.
|
|
548
|
+
The `effect` function provides a special `access` parameter with `tracked` and `ascend` functions that restore the active effect context for dependency tracking in asynchronous operations. In modern `mutts`, this is powered by the **Zone system**. When zones are registered in `asyncZone`, the active effect context is automatically preserved across `await` points and timers, making manual use of `tracked` optional for these cases.
|
|
480
549
|
|
|
481
550
|
#### The Problem with Async Effects
|
|
482
551
|
|
|
483
|
-
Traditionally, in JavaScript, async functions lose their context when they yield control.
|
|
552
|
+
Traditionally, in JavaScript, async functions lose their context when they yield control.
|
|
484
553
|
|
|
485
554
|
#### Understanding the active effect context
|
|
486
555
|
|
|
@@ -497,36 +566,28 @@ effect(() => {
|
|
|
497
566
|
// Async effect WITHOUT zone registration - context is lost after await
|
|
498
567
|
effect(async () => {
|
|
499
568
|
const value = state.count // ✅ Tracked
|
|
500
|
-
|
|
501
|
-
await someAsyncOperation()
|
|
502
|
-
|
|
503
|
-
// context is lost!
|
|
569
|
+
await someAsyncOperation() // context is lost!
|
|
504
570
|
const another = state.name // ❌ NOT tracked
|
|
505
571
|
})
|
|
506
572
|
|
|
507
573
|
// Async effect WITH zone registration - context is preserved
|
|
508
574
|
effect(async () => {
|
|
509
575
|
const value = state.count // ✅ Tracked
|
|
510
|
-
|
|
511
|
-
await someAsyncOperation()
|
|
512
|
-
|
|
513
|
-
// context is automatically restored!
|
|
576
|
+
await someAsyncOperation() // context is automatically restored!
|
|
514
577
|
const another = state.name // ✅ Tracked
|
|
515
578
|
})
|
|
516
579
|
|
|
517
580
|
// Using access.tracked() for manual restoration
|
|
518
581
|
effect(async ({ tracked }) => {
|
|
519
|
-
await someAsyncOperation()
|
|
520
|
-
|
|
521
|
-
// Useful for non-patched APIs or explicit scoping
|
|
582
|
+
await someAsyncOperation() // Useful for non-patched APIs or explicit scoping
|
|
522
583
|
const another = tracked(() => state.name) // ✅ Tracked
|
|
523
584
|
})
|
|
524
585
|
```
|
|
525
586
|
|
|
526
587
|
#### Key Benefits of the Zone System
|
|
527
588
|
|
|
528
|
-
1.
|
|
529
|
-
2.
|
|
589
|
+
1. **Automatic Restoration**: With zones registered in `asyncZone`, most native async APIs (Promises, timers) automatically preserve the reactive context.
|
|
590
|
+
2. **Manual Control**: `access.tracked()` allows you to manually "passport" the context into third-party libraries or unmanaged callbacks.
|
|
530
591
|
|
|
531
592
|
### Using `ascend` for Parent Effect Tracking
|
|
532
593
|
|
|
@@ -562,18 +623,21 @@ effect(({ ascend }) => {
|
|
|
562
623
|
Effects can be created inside other effects and will have separate effect scopes:
|
|
563
624
|
|
|
564
625
|
```typescript
|
|
565
|
-
import { effect, reactive } from 'mutts
|
|
626
|
+
import { effect, reactive } from 'mutts'
|
|
566
627
|
|
|
567
|
-
const state = reactive({
|
|
628
|
+
const state = reactive({
|
|
629
|
+
a: 0,
|
|
630
|
+
b: 0
|
|
631
|
+
})
|
|
568
632
|
|
|
569
633
|
const stopOuter = effect(() => {
|
|
570
634
|
state.a
|
|
571
|
-
|
|
635
|
+
|
|
572
636
|
// Create an inner effect with its own scope
|
|
573
637
|
const stopInner = effect(() => {
|
|
574
638
|
state.b
|
|
575
639
|
})
|
|
576
|
-
|
|
640
|
+
|
|
577
641
|
// Return cleanup function for the inner effect
|
|
578
642
|
return stopInner
|
|
579
643
|
})
|
|
@@ -584,21 +648,25 @@ const stopOuter = effect(() => {
|
|
|
584
648
|
The `untracked()` function allows you to run code without tracking dependencies, which can be useful for creating effects or performing operations that shouldn't be part of the current effect's dependency graph.
|
|
585
649
|
|
|
586
650
|
```typescript
|
|
587
|
-
import { effect, untracked, reactive } from 'mutts
|
|
651
|
+
import { effect, untracked, reactive } from 'mutts'
|
|
588
652
|
|
|
589
|
-
const state = reactive({
|
|
653
|
+
const state = reactive({
|
|
654
|
+
a: 0,
|
|
655
|
+
b: 0
|
|
656
|
+
})
|
|
590
657
|
|
|
591
658
|
effect(() => {
|
|
592
659
|
state.a
|
|
593
|
-
|
|
660
|
+
|
|
594
661
|
// Create an inner effect without tracking the creation under the outer effect
|
|
595
662
|
let stopInner: (() => void) | undefined
|
|
663
|
+
|
|
596
664
|
untracked(() => {
|
|
597
665
|
stopInner = effect(() => {
|
|
598
666
|
state.b
|
|
599
667
|
})
|
|
600
668
|
})
|
|
601
|
-
|
|
669
|
+
|
|
602
670
|
// Optionally stop it immediately to avoid accumulating watchers
|
|
603
671
|
stopInner && stopInner()
|
|
604
672
|
})
|
|
@@ -614,7 +682,7 @@ effect(() => {
|
|
|
614
682
|
Configure the reactive system behavior:
|
|
615
683
|
|
|
616
684
|
```typescript
|
|
617
|
-
import { options as reactiveOptions } from 'mutts
|
|
685
|
+
import { options as reactiveOptions } from 'mutts'
|
|
618
686
|
|
|
619
687
|
// Set maximum effect chain depth
|
|
620
688
|
reactiveOptions.maxEffectChain = 50
|
|
@@ -640,9 +708,14 @@ The `effect` function provides convenient shortcut modifiers for common options.
|
|
|
640
708
|
Creates an opaque effect that tracks object references rather than deep content. This is useful when you want effects to re-run only when the object identity changes, not when its properties change.
|
|
641
709
|
|
|
642
710
|
```typescript
|
|
643
|
-
import { effect, reactive } from 'mutts
|
|
711
|
+
import { effect, reactive } from 'mutts'
|
|
644
712
|
|
|
645
|
-
const item = reactive({
|
|
713
|
+
const item = reactive({
|
|
714
|
+
id: 1,
|
|
715
|
+
data: {
|
|
716
|
+
value: 10
|
|
717
|
+
}
|
|
718
|
+
})
|
|
646
719
|
|
|
647
720
|
// Regular effect - triggers on any property change
|
|
648
721
|
effect(() => {
|
|
@@ -654,8 +727,9 @@ effect.opaque(() => {
|
|
|
654
727
|
console.log('Data object:', item.data) // Only triggers when item.data is replaced
|
|
655
728
|
})
|
|
656
729
|
|
|
657
|
-
item.data.value = 20
|
|
658
|
-
|
|
730
|
+
item.data.value = 20 // Triggers regular effect, NOT opaque effect
|
|
731
|
+
|
|
732
|
+
item.data = { value: 30 } // Triggers BOTH effects
|
|
659
733
|
```
|
|
660
734
|
|
|
661
735
|
**Use cases for opaque effects:**
|
|
@@ -668,9 +742,11 @@ item.data = { value: 30 } // Triggers BOTH effects
|
|
|
668
742
|
Creates a named effect for easier debugging and profiling. The name appears in DevTools and debug logs.
|
|
669
743
|
|
|
670
744
|
```typescript
|
|
671
|
-
import { effect, reactive } from 'mutts
|
|
745
|
+
import { effect, reactive } from 'mutts'
|
|
672
746
|
|
|
673
|
-
const state = reactive({
|
|
747
|
+
const state = reactive({
|
|
748
|
+
count: 0
|
|
749
|
+
})
|
|
674
750
|
|
|
675
751
|
// Create a named effect
|
|
676
752
|
effect.named('counter-effect')(() => {
|
|
@@ -719,13 +795,14 @@ createDataEffect(() => {
|
|
|
719
795
|
})
|
|
720
796
|
```
|
|
721
797
|
|
|
798
|
+
## Class Reactivity
|
|
722
799
|
|
|
723
800
|
### `@reactive` Decorator
|
|
724
801
|
|
|
725
802
|
The `@reactive` decorator makes class instances automatically reactive. This is the recommended approach for adding reactivity to classes.
|
|
726
803
|
|
|
727
804
|
```typescript
|
|
728
|
-
import { reactive } from 'mutts
|
|
805
|
+
import { reactive } from 'mutts'
|
|
729
806
|
|
|
730
807
|
@reactive
|
|
731
808
|
class User {
|
|
@@ -757,7 +834,7 @@ user.name = "Jane" // Triggers effect
|
|
|
757
834
|
You can also use the functional syntax for making classes reactive:
|
|
758
835
|
|
|
759
836
|
```typescript
|
|
760
|
-
import { reactive } from 'mutts
|
|
837
|
+
import { reactive } from 'mutts'
|
|
761
838
|
|
|
762
839
|
class User {
|
|
763
840
|
name: string
|
|
@@ -789,11 +866,14 @@ user.name = "Jane" // Triggers effect
|
|
|
789
866
|
For complex inheritance trees, especially when you need to solve constructor reactivity issues, extend `ReactiveBase`:
|
|
790
867
|
|
|
791
868
|
```typescript
|
|
792
|
-
import { ReactiveBase, reactive } from 'mutts
|
|
869
|
+
import { ReactiveBase, reactive } from 'mutts'
|
|
793
870
|
|
|
794
871
|
class GameObject extends ReactiveBase {
|
|
795
872
|
id = 'game-object'
|
|
796
|
-
position = {
|
|
873
|
+
position = {
|
|
874
|
+
x: 0,
|
|
875
|
+
y: 0
|
|
876
|
+
}
|
|
797
877
|
}
|
|
798
878
|
|
|
799
879
|
class Entity extends GameObject {
|
|
@@ -813,11 +893,10 @@ effect(() => {
|
|
|
813
893
|
})
|
|
814
894
|
|
|
815
895
|
player.position.x = 10 // Triggers effect
|
|
816
|
-
player.health = 80
|
|
896
|
+
player.health = 80 // Triggers effect
|
|
817
897
|
```
|
|
818
898
|
|
|
819
899
|
**Advantages of `ReactiveBase`:**
|
|
820
|
-
|
|
821
900
|
1. **Constructor Reactivity**: Solves the issue where `this` in the constructor is not yet reactive
|
|
822
901
|
2. **Inheritance Safety**: Prevents reactivity from being added to prototype chains in complex inheritance trees
|
|
823
902
|
3. **No Side Effects**: The base class itself has no effect - it only enables proper reactivity when combined with `@reactive`
|
|
@@ -829,7 +908,7 @@ player.health = 80 // Triggers effect
|
|
|
829
908
|
- You want the cleanest, most modern syntax
|
|
830
909
|
- You don't need to modify or use `this` in the constructor
|
|
831
910
|
|
|
832
|
-
**Use `ReactiveBase
|
|
911
|
+
**Use `ReactiveBase`+`@reactive` when:**
|
|
833
912
|
- You have complex inheritance trees (like game objects, UI components)
|
|
834
913
|
- You need to modify or use `this` in the constructor
|
|
835
914
|
- You want to prevent reactivity from being added to prototype chains
|
|
@@ -884,7 +963,7 @@ effect(() => {
|
|
|
884
963
|
console.log('Cart items:', cart.items)
|
|
885
964
|
})
|
|
886
965
|
|
|
887
|
-
cart.addItem('Apple')
|
|
966
|
+
cart.addItem('Apple') // Triggers effect
|
|
888
967
|
cart.removeItem('Apple') // Triggers effect
|
|
889
968
|
```
|
|
890
969
|
|
|
@@ -937,15 +1016,23 @@ function unreactive(target: Constructor<T>): Constructor<T>
|
|
|
937
1016
|
|
|
938
1017
|
```typescript
|
|
939
1018
|
// Mark individual object as non-reactive
|
|
940
|
-
const obj = {
|
|
1019
|
+
const obj = {
|
|
1020
|
+
count: 0
|
|
1021
|
+
}
|
|
1022
|
+
|
|
941
1023
|
unreactive(obj)
|
|
1024
|
+
|
|
942
1025
|
const reactiveObj = reactive(obj) // Returns obj unchanged
|
|
943
1026
|
|
|
944
1027
|
// Mark entire class as non-reactive
|
|
945
1028
|
class Utility {
|
|
946
|
-
static helper() {
|
|
1029
|
+
static helper() {
|
|
1030
|
+
return 'help'
|
|
1031
|
+
}
|
|
947
1032
|
}
|
|
1033
|
+
|
|
948
1034
|
unreactive(Utility)
|
|
1035
|
+
|
|
949
1036
|
const instance = new Utility()
|
|
950
1037
|
const reactiveInstance = reactive(instance) // Returns instance unchanged
|
|
951
1038
|
```
|
|
@@ -959,7 +1046,6 @@ Mark class properties as non-reactive using class-level syntax.
|
|
|
959
1046
|
@unreactive('id')
|
|
960
1047
|
class User {
|
|
961
1048
|
id: string = 'user-123'
|
|
962
|
-
|
|
963
1049
|
name: string = 'John'
|
|
964
1050
|
age: number = 30
|
|
965
1051
|
}
|
|
@@ -968,7 +1054,7 @@ const user = new User()
|
|
|
968
1054
|
|
|
969
1055
|
effect(() => {
|
|
970
1056
|
console.log(user.name, user.age) // Tracks these
|
|
971
|
-
console.log(user.id)
|
|
1057
|
+
console.log(user.id) // Does NOT track this
|
|
972
1058
|
})
|
|
973
1059
|
|
|
974
1060
|
user.name = 'Jane' // Triggers effect
|
|
@@ -1070,23 +1156,29 @@ Non-reactive objects can improve performance:
|
|
|
1070
1156
|
```typescript
|
|
1071
1157
|
// Good: Mark large, rarely-changing objects as non-reactive
|
|
1072
1158
|
const config = unreactive({
|
|
1073
|
-
apiEndpoints: {
|
|
1074
|
-
|
|
1159
|
+
apiEndpoints: {
|
|
1160
|
+
/* large config object */
|
|
1161
|
+
},
|
|
1162
|
+
featureFlags: {
|
|
1163
|
+
/* many flags */
|
|
1164
|
+
}
|
|
1075
1165
|
})
|
|
1076
1166
|
|
|
1077
1167
|
// Good: Mark utility classes as non-reactive
|
|
1078
1168
|
class MathUtils {
|
|
1079
1169
|
static PI = 3.14159
|
|
1080
|
-
|
|
1170
|
+
|
|
1171
|
+
static square(x: number) {
|
|
1172
|
+
return x * x
|
|
1173
|
+
}
|
|
1081
1174
|
}
|
|
1175
|
+
|
|
1082
1176
|
unreactive(MathUtils)
|
|
1083
1177
|
|
|
1084
1178
|
// Good: Mark properties that don't need reactivity
|
|
1085
1179
|
class User {
|
|
1086
|
-
@unreactive
|
|
1087
|
-
metadata: any = {} // Large metadata object
|
|
1180
|
+
@unreactive metadata: any = {} // Large metadata object
|
|
1088
1181
|
|
|
1089
1182
|
name: string = 'John' // This should be reactive
|
|
1090
1183
|
}
|
|
1091
|
-
```
|
|
1092
|
-
|
|
1184
|
+
```
|