@ersbeth/picoflow 2.0.2 → 2.0.3
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 +10 -0
- package/SKILL.md +106 -0
- package/package.json +5 -1
- package/.gitlab-ci.yml +0 -24
- package/.vscode/settings.json +0 -5
- package/CHANGELOG.md +0 -94
- package/biome.json +0 -47
- package/docs/.vitepress/config.mts +0 -145
- package/docs/api/functions/array.md +0 -35
- package/docs/api/functions/constant.md +0 -33
- package/docs/api/functions/constantAsync.md +0 -69
- package/docs/api/functions/derivation.md +0 -34
- package/docs/api/functions/derivationAsync.md +0 -34
- package/docs/api/functions/from.md +0 -129
- package/docs/api/functions/isDisposable.md +0 -27
- package/docs/api/functions/map.md +0 -36
- package/docs/api/functions/signal.md +0 -21
- package/docs/api/functions/state.md +0 -67
- package/docs/api/functions/stateAsync.md +0 -69
- package/docs/api/functions/subscribe.md +0 -40
- package/docs/api/functions/writableDerivation.md +0 -33
- package/docs/api/functions/writableDerivationAsync.md +0 -34
- package/docs/api/index.md +0 -61
- package/docs/api/interfaces/FlowArray.md +0 -439
- package/docs/api/interfaces/FlowConstant.md +0 -220
- package/docs/api/interfaces/FlowConstantAsync.md +0 -221
- package/docs/api/interfaces/FlowDerivation.md +0 -241
- package/docs/api/interfaces/FlowDerivationAsync.md +0 -242
- package/docs/api/interfaces/FlowDisposable.md +0 -59
- package/docs/api/interfaces/FlowEffect.md +0 -64
- package/docs/api/interfaces/FlowMap.md +0 -374
- package/docs/api/interfaces/FlowObservable.md +0 -155
- package/docs/api/interfaces/FlowSignal.md +0 -156
- package/docs/api/interfaces/FlowState.md +0 -269
- package/docs/api/interfaces/FlowStateAsync.md +0 -268
- package/docs/api/interfaces/FlowSubscribable.md +0 -55
- package/docs/api/interfaces/FlowTracker.md +0 -61
- package/docs/api/interfaces/FlowValue.md +0 -222
- package/docs/api/interfaces/FlowWritableDerivation.md +0 -292
- package/docs/api/interfaces/FlowWritableDerivationAsync.md +0 -293
- package/docs/api/type-aliases/DerivationFunction.md +0 -28
- package/docs/api/type-aliases/DerivationFunctionAsync.md +0 -28
- package/docs/api/type-aliases/FlowArrayAction.md +0 -60
- package/docs/api/type-aliases/FlowDataTracker.md +0 -33
- package/docs/api/type-aliases/FlowMapAction.md +0 -48
- package/docs/api/type-aliases/FlowOnDataListener.md +0 -33
- package/docs/api/type-aliases/FlowOnErrorListener.md +0 -27
- package/docs/api/type-aliases/FlowOnPendingListener.md +0 -21
- package/docs/api/type-aliases/FlowReadonly.md +0 -22
- package/docs/api/type-aliases/InitFunction.md +0 -21
- package/docs/api/type-aliases/InitFunctionAsync.md +0 -21
- package/docs/api/type-aliases/NotPromise.md +0 -21
- package/docs/api/type-aliases/UpdateFunction.md +0 -27
- package/docs/api/type-aliases/UpdateFunctionAsync.md +0 -27
- package/docs/api/typedoc-sidebar.json +0 -65
- package/docs/examples/examples.md +0 -2311
- package/docs/examples/patterns.md +0 -649
- package/docs/guide/advanced/architecture.md +0 -1234
- package/docs/guide/advanced/disposal.md +0 -426
- package/docs/guide/advanced/migration-v1.md +0 -464
- package/docs/guide/advanced/migration-v2.md +0 -204
- package/docs/guide/advanced/solidjs.md +0 -135
- package/docs/guide/introduction/concepts.md +0 -57
- package/docs/guide/introduction/conventions.md +0 -30
- package/docs/guide/introduction/getting-started.md +0 -139
- package/docs/guide/introduction/lifecycle.md +0 -368
- package/docs/guide/primitives/array.md +0 -286
- package/docs/guide/primitives/constant.md +0 -207
- package/docs/guide/primitives/derivations.md +0 -281
- package/docs/guide/primitives/effects.md +0 -372
- package/docs/guide/primitives/map.md +0 -265
- package/docs/guide/primitives/overview.md +0 -92
- package/docs/guide/primitives/signal.md +0 -222
- package/docs/guide/primitives/state.md +0 -272
- package/docs/index.md +0 -47
- package/docs/public/logo.svg +0 -1
- package/src/api/base/flowDisposable.ts +0 -44
- package/src/api/base/flowObservable.ts +0 -28
- package/src/api/base/flowSubscribable.ts +0 -87
- package/src/api/base/flowTracker.ts +0 -7
- package/src/api/base/index.ts +0 -4
- package/src/api/index.ts +0 -2
- package/src/api/nodes/async/flowConstantAsync.ts +0 -36
- package/src/api/nodes/async/flowDerivationAsync.ts +0 -42
- package/src/api/nodes/async/flowStateAsync.ts +0 -47
- package/src/api/nodes/async/flowWritableDerivationAsync.ts +0 -33
- package/src/api/nodes/async/index.ts +0 -4
- package/src/api/nodes/collections/flowArray.ts +0 -155
- package/src/api/nodes/collections/flowMap.ts +0 -115
- package/src/api/nodes/collections/index.ts +0 -2
- package/src/api/nodes/flowEffect.ts +0 -42
- package/src/api/nodes/flowSignal.ts +0 -28
- package/src/api/nodes/flowValue.ts +0 -37
- package/src/api/nodes/index.ts +0 -7
- package/src/api/nodes/sync/flowConstant.ts +0 -33
- package/src/api/nodes/sync/flowDerivation.ts +0 -41
- package/src/api/nodes/sync/flowState.ts +0 -45
- package/src/api/nodes/sync/flowWritableDerivation.ts +0 -31
- package/src/api/nodes/sync/index.ts +0 -4
- package/src/api/nodes/utils.ts +0 -24
- package/src/base/disposable.ts +0 -18
- package/src/base/executionStack.ts +0 -42
- package/src/base/index.ts +0 -5
- package/src/base/node.ts +0 -98
- package/src/base/observable.ts +0 -92
- package/src/base/observer.ts +0 -51
- package/src/converters/index.ts +0 -1
- package/src/converters/solid.ts +0 -109
- package/src/index.ts +0 -2
- package/src/nodes/arrayNode.ts +0 -180
- package/src/nodes/effectNode.ts +0 -58
- package/src/nodes/index.ts +0 -7
- package/src/nodes/mapNode.ts +0 -125
- package/src/nodes/signalNode.ts +0 -19
- package/src/nodes/valueAsyncNode.ts +0 -85
- package/src/nodes/valueNode.ts +0 -148
- package/src/nodes/valueSyncNode.ts +0 -125
- package/src/schedulers/asyncResolver.ts +0 -78
- package/src/schedulers/asyncScheduler.ts +0 -66
- package/src/schedulers/index.ts +0 -4
- package/src/schedulers/pendingError.ts +0 -13
- package/src/schedulers/scheduler.ts +0 -9
- package/src/schedulers/syncResolver.ts +0 -69
- package/src/schedulers/syncScheduler.ts +0 -55
- package/test/base/pendingError.test.ts +0 -67
- package/test/converters/solid.derivation.browser.test.tsx +0 -69
- package/test/converters/solid.node.test.ts +0 -654
- package/test/converters/solid.state.browser.test.tsx +0 -1592
- package/test/reactivity/flowSignal.test.ts +0 -226
- package/test/reactivity/nodes/async/asyncScheduler/asyncResolver.test.ts +0 -593
- package/test/reactivity/nodes/async/asyncScheduler/asyncScheduler.test.ts +0 -317
- package/test/reactivity/nodes/async/flowConstantAsync.test.ts +0 -652
- package/test/reactivity/nodes/async/flowDerivation.test.ts +0 -898
- package/test/reactivity/nodes/async/flowDerivationAsync.test.ts +0 -1716
- package/test/reactivity/nodes/async/flowStateAsync.test.ts +0 -708
- package/test/reactivity/nodes/async/flowWritableDerivationAsync.test.ts +0 -614
- package/test/reactivity/nodes/collections/flowArray.asyncStates.test.ts +0 -1289
- package/test/reactivity/nodes/collections/flowArray.scalars.test.ts +0 -961
- package/test/reactivity/nodes/collections/flowArray.states.test.ts +0 -1035
- package/test/reactivity/nodes/collections/flowMap.asyncStates.test.ts +0 -960
- package/test/reactivity/nodes/collections/flowMap.scalars.test.ts +0 -775
- package/test/reactivity/nodes/collections/flowMap.states.test.ts +0 -958
- package/test/reactivity/nodes/sync/flowConstant.test.ts +0 -377
- package/test/reactivity/nodes/sync/flowDerivation.test.ts +0 -896
- package/test/reactivity/nodes/sync/flowState.test.ts +0 -341
- package/test/reactivity/nodes/sync/flowWritableDerivation.test.ts +0 -603
- package/test/vitest.d.ts +0 -10
- package/tsconfig.json +0 -37
- package/typedoc.json +0 -37
- package/vite.config.ts +0 -31
- package/vitest.browser.config.ts +0 -21
- package/vitest.config.ts +0 -17
|
@@ -1,426 +0,0 @@
|
|
|
1
|
-
# Disposal
|
|
2
|
-
|
|
3
|
-
Memory management is crucial in long-running applications. PicoFlow provides explicit disposal mechanisms to prevent memory leaks and ensure resources are properly cleaned up.
|
|
4
|
-
|
|
5
|
-
## Why Disposal Matters
|
|
6
|
-
|
|
7
|
-
All PicoFlow primitives are disposable. When you're done with them, you should clean them up:
|
|
8
|
-
|
|
9
|
-
```typescript
|
|
10
|
-
const $count = state(0)
|
|
11
|
-
const fx = effect((t) => {
|
|
12
|
-
console.log($count.get(t))
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
// Later... clean up
|
|
16
|
-
fx.dispose() // Effect stops running
|
|
17
|
-
$count.dispose() // State is cleaned up
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
**Why disposal matters:**
|
|
21
|
-
- **Prevents memory leaks** - Unreferenced but undisposed primitives stay in memory
|
|
22
|
-
- **Stops effects from running** - Effects continue executing until explicitly disposed
|
|
23
|
-
- **Cleans up resources** - WebSocket connections, intervals, event listeners, etc.
|
|
24
|
-
- **Important in long-running applications** - SPAs, dashboards, real-time apps
|
|
25
|
-
|
|
26
|
-
```mermaid
|
|
27
|
-
flowchart LR
|
|
28
|
-
A[Create primitive] --> B[Use in effects]
|
|
29
|
-
B --> C[No longer needed]
|
|
30
|
-
C --> D{Disposed?}
|
|
31
|
-
D -->|Yes| E[Memory freed]
|
|
32
|
-
D -->|No| F[Memory leak!]
|
|
33
|
-
|
|
34
|
-
style E fill:#90EE90
|
|
35
|
-
style F fill:#FFB6C6
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## When to Dispose
|
|
39
|
-
|
|
40
|
-
### Component Cleanup
|
|
41
|
-
|
|
42
|
-
In component-based architectures, dispose primitives when components unmount:
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
class TodoList {
|
|
46
|
-
private $todos = state<Todo[]>([])
|
|
47
|
-
private disposables: FlowDisposable[] = []
|
|
48
|
-
|
|
49
|
-
constructor() {
|
|
50
|
-
// Track all disposables
|
|
51
|
-
this.disposables.push(
|
|
52
|
-
effect((t) => {
|
|
53
|
-
this.render($todos.get(t))
|
|
54
|
-
})
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
destroy() {
|
|
59
|
-
// Clean up when component is destroyed
|
|
60
|
-
this.disposables.forEach(d => d.dispose())
|
|
61
|
-
this.$todos.dispose()
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Conditional Effects
|
|
67
|
-
|
|
68
|
-
Dispose and recreate effects based on application state:
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
let currentEffect: FlowEffect | null = null
|
|
72
|
-
|
|
73
|
-
function enableLogging(enable: boolean) {
|
|
74
|
-
// Dispose previous effect
|
|
75
|
-
currentEffect?.dispose()
|
|
76
|
-
|
|
77
|
-
if (enable) {
|
|
78
|
-
currentEffect = effect((t) => {
|
|
79
|
-
console.log('Value:', $data.get(t))
|
|
80
|
-
})
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Toggle logging on/off
|
|
85
|
-
enableLogging(true) // Effect starts
|
|
86
|
-
enableLogging(false) // Effect disposed
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Resource Management
|
|
90
|
-
|
|
91
|
-
Dispose primitives that manage external resources:
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
const $wsData = stream<string>((set) => {
|
|
95
|
-
const ws = new WebSocket('ws://example.com')
|
|
96
|
-
ws.onmessage = (e) => set(e.data)
|
|
97
|
-
|
|
98
|
-
// Cleanup function called on disposal
|
|
99
|
-
return () => ws.close()
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
// Use the stream
|
|
103
|
-
effect((t) => {
|
|
104
|
-
console.log('Message:', $wsData.get(t))
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
// Later... close the connection
|
|
108
|
-
$wsData.dispose()
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### Interval Cleanup
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
const $tick = stream<number>((set) => {
|
|
115
|
-
let count = 0
|
|
116
|
-
const interval = setInterval(() => {
|
|
117
|
-
set(count++)
|
|
118
|
-
}, 1000)
|
|
119
|
-
|
|
120
|
-
// Cleanup: clear interval on disposal
|
|
121
|
-
return () => clearInterval(interval)
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
// Later...
|
|
125
|
-
$tick.dispose() // Interval cleared
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
## Automatic Cleanup
|
|
129
|
-
|
|
130
|
-
Effects automatically clean up their dependencies when disposed:
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
const $a = state(1)
|
|
134
|
-
const $b = state(2)
|
|
135
|
-
|
|
136
|
-
const fx = effect((t) => {
|
|
137
|
-
console.log($a.get(t) + $b.get(t))
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
fx.dispose()
|
|
141
|
-
|
|
142
|
-
// These no longer trigger the effect
|
|
143
|
-
$a.set(10) // No console log
|
|
144
|
-
$b.set(20) // No console log
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
When an effect is disposed:
|
|
148
|
-
1. It unregisters from all its dependencies
|
|
149
|
-
2. Future changes to those dependencies won't trigger it
|
|
150
|
-
3. The effect's memory is freed
|
|
151
|
-
|
|
152
|
-
## Best Practices
|
|
153
|
-
|
|
154
|
-
### 1. Always Dispose in Cleanup Hooks
|
|
155
|
-
|
|
156
|
-
In any framework with lifecycle hooks, dispose in the cleanup phase:
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
// React
|
|
160
|
-
useEffect(() => {
|
|
161
|
-
const fx = effect((t) => {
|
|
162
|
-
// ... effect code
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
return () => fx.dispose() // Cleanup
|
|
166
|
-
}, [])
|
|
167
|
-
|
|
168
|
-
// Vue
|
|
169
|
-
onUnmounted(() => {
|
|
170
|
-
fx.dispose()
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
// Svelte
|
|
174
|
-
onDestroy(() => {
|
|
175
|
-
fx.dispose()
|
|
176
|
-
})
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### 2. Group Related Disposables
|
|
180
|
-
|
|
181
|
-
Use collections to manage multiple disposables:
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
class DataManager {
|
|
185
|
-
private disposables = new Set<FlowDisposable>()
|
|
186
|
-
|
|
187
|
-
track(disposable: FlowDisposable) {
|
|
188
|
-
this.disposables.add(disposable)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
cleanup() {
|
|
192
|
-
this.disposables.forEach(d => d.dispose())
|
|
193
|
-
this.disposables.clear()
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Usage
|
|
198
|
-
const manager = new DataManager()
|
|
199
|
-
manager.track(effect((t) => { /* ... */ }))
|
|
200
|
-
manager.track(effect((t) => { /* ... */ }))
|
|
201
|
-
manager.track($someState)
|
|
202
|
-
|
|
203
|
-
// Clean up everything
|
|
204
|
-
manager.cleanup()
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### 3. Dispose in Reverse Order of Creation
|
|
208
|
-
|
|
209
|
-
Dispose dependents before dependencies:
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
const $data = state(0)
|
|
213
|
-
const $derived = derivation((t) => $data.get(t) * 2)
|
|
214
|
-
const fx = effect((t) => console.log($derived.get(t)))
|
|
215
|
-
|
|
216
|
-
// Cleanup: dispose in reverse order
|
|
217
|
-
fx.dispose() // Effect first (depends on $derived)
|
|
218
|
-
$derived.dispose() // Derivation second (depends on $data)
|
|
219
|
-
$data.dispose() // State last (no dependencies)
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
**Why?** Disposing in reverse order prevents:
|
|
223
|
-
- Accessing disposed dependencies
|
|
224
|
-
- Unnecessary recomputations during cleanup
|
|
225
|
-
- Race conditions during shutdown
|
|
226
|
-
|
|
227
|
-
### 4. Check Disposal State
|
|
228
|
-
|
|
229
|
-
Use the `disposed` property to check if a primitive has been disposed:
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
const fx = effect((t) => {
|
|
233
|
-
console.log($count.get(t))
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
console.log(fx.disposed) // false
|
|
237
|
-
|
|
238
|
-
fx.dispose()
|
|
239
|
-
|
|
240
|
-
console.log(fx.disposed) // true
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### 5. Handle Disposal Errors
|
|
244
|
-
|
|
245
|
-
Wrap disposal in try-catch for robust cleanup:
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
function cleanupResources(disposables: FlowDisposable[]) {
|
|
249
|
-
const errors: Error[] = []
|
|
250
|
-
|
|
251
|
-
for (const disposable of disposables) {
|
|
252
|
-
try {
|
|
253
|
-
disposable.dispose()
|
|
254
|
-
} catch (error) {
|
|
255
|
-
errors.push(error as Error)
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (errors.length > 0) {
|
|
260
|
-
console.error('Disposal errors:', errors)
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
## Common Pitfalls
|
|
266
|
-
|
|
267
|
-
### Pitfall 1: Not Disposing Effects
|
|
268
|
-
|
|
269
|
-
```typescript
|
|
270
|
-
// ❌ Memory leak - effect never disposed
|
|
271
|
-
function updateCounter() {
|
|
272
|
-
effect((t) => {
|
|
273
|
-
document.getElementById('count').textContent = $count.get(t)
|
|
274
|
-
})
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Called 100 times = 100 effects running!
|
|
278
|
-
for (let i = 0; i < 100; i++) {
|
|
279
|
-
updateCounter()
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// ✅ Proper cleanup
|
|
283
|
-
function updateCounter(): () => void {
|
|
284
|
-
const fx = effect((t) => {
|
|
285
|
-
document.getElementById('count').textContent = $count.get(t)
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
return () => fx.dispose()
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const cleanup = updateCounter()
|
|
292
|
-
// Later...
|
|
293
|
-
cleanup()
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
### Pitfall 2: Disposing Too Early
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
// ❌ Disposed too early
|
|
300
|
-
const fx = effect((t) => {
|
|
301
|
-
console.log($count.get(t))
|
|
302
|
-
})
|
|
303
|
-
|
|
304
|
-
fx.dispose() // Effect disposed immediately!
|
|
305
|
-
|
|
306
|
-
$count.set(1) // No effect runs
|
|
307
|
-
|
|
308
|
-
// ✅ Dispose at the right time
|
|
309
|
-
const fx = effect((t) => {
|
|
310
|
-
console.log($count.get(t))
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
// Use the effect...
|
|
314
|
-
$count.set(1) // Effect runs
|
|
315
|
-
|
|
316
|
-
// Now dispose when truly done
|
|
317
|
-
fx.dispose()
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
### Pitfall 3: Forgetting Stream Cleanup
|
|
321
|
-
|
|
322
|
-
```typescript
|
|
323
|
-
// ❌ WebSocket never closed
|
|
324
|
-
function connectWebSocket() {
|
|
325
|
-
const $ws = stream<string>((set) => {
|
|
326
|
-
const socket = new WebSocket('ws://...')
|
|
327
|
-
socket.onmessage = (e) => set(e.data)
|
|
328
|
-
// Missing: return () => socket.close()
|
|
329
|
-
})
|
|
330
|
-
return $ws
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// ✅ Proper cleanup
|
|
334
|
-
function connectWebSocket() {
|
|
335
|
-
const $ws = stream<string>((set) => {
|
|
336
|
-
const socket = new WebSocket('ws://...')
|
|
337
|
-
socket.onmessage = (e) => set(e.data)
|
|
338
|
-
|
|
339
|
-
return () => socket.close() // Cleanup function
|
|
340
|
-
})
|
|
341
|
-
return $ws
|
|
342
|
-
}
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
## Disposal Patterns
|
|
346
|
-
|
|
347
|
-
### Pattern 1: Disposable Builder
|
|
348
|
-
|
|
349
|
-
```typescript
|
|
350
|
-
class DisposableBuilder {
|
|
351
|
-
private disposables: FlowDisposable[] = []
|
|
352
|
-
|
|
353
|
-
add<T extends FlowDisposable>(disposable: T): T {
|
|
354
|
-
this.disposables.push(disposable)
|
|
355
|
-
return disposable
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
disposeAll(): void {
|
|
359
|
-
// Dispose in reverse order
|
|
360
|
-
for (let i = this.disposables.length - 1; i >= 0; i--) {
|
|
361
|
-
this.disposables[i].dispose()
|
|
362
|
-
}
|
|
363
|
-
this.disposables = []
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Usage
|
|
368
|
-
const builder = new DisposableBuilder()
|
|
369
|
-
builder.add(effect((t) => { /* ... */ }))
|
|
370
|
-
builder.add(effect((t) => { /* ... */ }))
|
|
371
|
-
builder.disposeAll()
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
### Pattern 2: Scoped Disposables
|
|
375
|
-
|
|
376
|
-
```typescript
|
|
377
|
-
function withDisposables<T>(
|
|
378
|
-
fn: (track: <D extends FlowDisposable>(d: D) => D) => T
|
|
379
|
-
): T & { cleanup: () => void } {
|
|
380
|
-
const disposables: FlowDisposable[] = []
|
|
381
|
-
|
|
382
|
-
const track = <D extends FlowDisposable>(d: D): D => {
|
|
383
|
-
disposables.push(d)
|
|
384
|
-
return d
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
const result = fn(track) as T & { cleanup: () => void }
|
|
388
|
-
result.cleanup = () => disposables.forEach(d => d.dispose())
|
|
389
|
-
|
|
390
|
-
return result
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Usage
|
|
394
|
-
const { cleanup } = withDisposables((track) => {
|
|
395
|
-
const $count = track(state(0))
|
|
396
|
-
const fx = track(effect((t) => console.log($count.get(t))))
|
|
397
|
-
})
|
|
398
|
-
|
|
399
|
-
// Clean up everything
|
|
400
|
-
cleanup()
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
### Pattern 3: Auto-disposal on Condition
|
|
404
|
-
|
|
405
|
-
```typescript
|
|
406
|
-
function createAutoDisposeEffect(
|
|
407
|
-
condition: () => boolean,
|
|
408
|
-
apply: (t: TrackingContext) => void
|
|
409
|
-
): FlowEffect {
|
|
410
|
-
const fx = effect((t) => {
|
|
411
|
-
if (!condition()) {
|
|
412
|
-
fx.dispose()
|
|
413
|
-
return
|
|
414
|
-
}
|
|
415
|
-
apply(t)
|
|
416
|
-
})
|
|
417
|
-
return fx
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Usage: effect auto-disposes when count > 10
|
|
421
|
-
createAutoDisposeEffect(
|
|
422
|
-
() => $count.pick() <= 10,
|
|
423
|
-
(t) => console.log('Count:', $count.get(t))
|
|
424
|
-
)
|
|
425
|
-
```
|
|
426
|
-
|