@ersbeth/picoflow 0.2.4 → 1.0.0
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/.cursor/plans/update-js-e795d61b.plan.md +567 -0
- package/.gitlab-ci.yml +24 -0
- package/.vscode/settings.json +3 -3
- package/CHANGELOG.md +51 -0
- package/IMPLEMENTATION_GUIDE.md +1578 -0
- package/README.md +62 -25
- package/biome.json +32 -32
- package/dist/picoflow.js +610 -436
- package/dist/types/advanced/array.d.ts +0 -6
- package/dist/types/advanced/array.d.ts.map +1 -1
- package/dist/types/advanced/index.d.ts +5 -5
- package/dist/types/advanced/index.d.ts.map +1 -1
- package/dist/types/advanced/map.d.ts +114 -23
- package/dist/types/advanced/map.d.ts.map +1 -1
- package/dist/types/advanced/resource.d.ts +51 -12
- package/dist/types/advanced/resource.d.ts.map +1 -1
- package/dist/types/advanced/resourceAsync.d.ts +28 -13
- package/dist/types/advanced/resourceAsync.d.ts.map +1 -1
- package/dist/types/advanced/stream.d.ts +74 -16
- package/dist/types/advanced/stream.d.ts.map +1 -1
- package/dist/types/advanced/streamAsync.d.ts +69 -15
- package/dist/types/advanced/streamAsync.d.ts.map +1 -1
- package/dist/types/basic/constant.d.ts +44 -16
- package/dist/types/basic/constant.d.ts.map +1 -1
- package/dist/types/basic/derivation.d.ts +73 -24
- package/dist/types/basic/derivation.d.ts.map +1 -1
- package/dist/types/basic/disposable.d.ts +65 -6
- package/dist/types/basic/disposable.d.ts.map +1 -1
- package/dist/types/basic/effect.d.ts +27 -16
- package/dist/types/basic/effect.d.ts.map +1 -1
- package/dist/types/basic/index.d.ts +7 -8
- package/dist/types/basic/index.d.ts.map +1 -1
- package/dist/types/basic/observable.d.ts +62 -13
- package/dist/types/basic/observable.d.ts.map +1 -1
- package/dist/types/basic/signal.d.ts +35 -6
- package/dist/types/basic/signal.d.ts.map +1 -1
- package/dist/types/basic/state.d.ts +25 -4
- package/dist/types/basic/state.d.ts.map +1 -1
- package/dist/types/basic/trackingContext.d.ts +33 -0
- package/dist/types/basic/trackingContext.d.ts.map +1 -0
- package/dist/types/creators.d.ts +271 -26
- package/dist/types/creators.d.ts.map +1 -1
- package/dist/types/index.d.ts +60 -7
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/solid/converters.d.ts +5 -5
- package/dist/types/solid/converters.d.ts.map +1 -1
- package/dist/types/solid/index.d.ts +2 -2
- package/dist/types/solid/index.d.ts.map +1 -1
- package/dist/types/solid/primitives.d.ts +96 -4
- package/dist/types/solid/primitives.d.ts.map +1 -1
- package/docs/.vitepress/config.mts +110 -0
- package/docs/api/classes/FlowArray.md +489 -0
- package/docs/api/classes/FlowConstant.md +350 -0
- package/docs/api/classes/FlowDerivation.md +334 -0
- package/docs/api/classes/FlowEffect.md +100 -0
- package/docs/api/classes/FlowMap.md +512 -0
- package/docs/api/classes/FlowObservable.md +306 -0
- package/docs/api/classes/FlowResource.md +380 -0
- package/docs/api/classes/FlowResourceAsync.md +362 -0
- package/docs/api/classes/FlowSignal.md +160 -0
- package/docs/api/classes/FlowState.md +368 -0
- package/docs/api/classes/FlowStream.md +367 -0
- package/docs/api/classes/FlowStreamAsync.md +364 -0
- package/docs/api/classes/SolidDerivation.md +75 -0
- package/docs/api/classes/SolidResource.md +91 -0
- package/docs/api/classes/SolidState.md +71 -0
- package/docs/api/classes/TrackingContext.md +33 -0
- package/docs/api/functions/array.md +58 -0
- package/docs/api/functions/constant.md +45 -0
- package/docs/api/functions/derivation.md +53 -0
- package/docs/api/functions/effect.md +49 -0
- package/docs/api/functions/from.md +220 -0
- package/docs/api/functions/isDisposable.md +49 -0
- package/docs/api/functions/map.md +57 -0
- package/docs/api/functions/resource.md +52 -0
- package/docs/api/functions/resourceAsync.md +50 -0
- package/docs/api/functions/signal.md +36 -0
- package/docs/api/functions/state.md +47 -0
- package/docs/api/functions/stream.md +53 -0
- package/docs/api/functions/streamAsync.md +50 -0
- package/docs/api/index.md +118 -0
- package/docs/api/interfaces/FlowDisposable.md +65 -0
- package/docs/api/interfaces/SolidObservable.md +19 -0
- package/docs/api/type-aliases/FlowArrayAction.md +49 -0
- package/docs/api/type-aliases/FlowStreamDisposer.md +15 -0
- package/docs/api/type-aliases/FlowStreamSetter.md +27 -0
- package/docs/api/type-aliases/FlowStreamUpdater.md +32 -0
- package/docs/api/type-aliases/NotPromise.md +18 -0
- package/docs/api/type-aliases/SolidGetter.md +17 -0
- package/docs/api/typedoc-sidebar.json +1 -0
- package/docs/examples/examples.md +2313 -0
- package/docs/examples/patterns.md +649 -0
- package/docs/guide/advanced/disposal.md +426 -0
- package/docs/guide/advanced/solidjs.md +221 -0
- package/docs/guide/advanced/upgrading.md +464 -0
- package/docs/guide/introduction/concepts.md +56 -0
- package/docs/guide/introduction/conventions.md +61 -0
- package/docs/guide/introduction/getting-started.md +134 -0
- package/docs/guide/introduction/lifecycle.md +371 -0
- package/docs/guide/primitives/array.md +400 -0
- package/docs/guide/primitives/constant.md +380 -0
- package/docs/guide/primitives/derivations.md +348 -0
- package/docs/guide/primitives/effects.md +458 -0
- package/docs/guide/primitives/map.md +387 -0
- package/docs/guide/primitives/overview.md +175 -0
- package/docs/guide/primitives/resources.md +858 -0
- package/docs/guide/primitives/signal.md +259 -0
- package/docs/guide/primitives/state.md +368 -0
- package/docs/guide/primitives/streams.md +931 -0
- package/docs/index.md +47 -0
- package/docs/public/logo.svg +1 -0
- package/package.json +57 -41
- package/src/advanced/array.ts +208 -210
- package/src/advanced/index.ts +7 -7
- package/src/advanced/map.ts +178 -68
- package/src/advanced/resource.ts +87 -43
- package/src/advanced/resourceAsync.ts +62 -42
- package/src/advanced/stream.ts +113 -50
- package/src/advanced/streamAsync.ts +120 -61
- package/src/basic/constant.ts +82 -49
- package/src/basic/derivation.ts +128 -84
- package/src/basic/disposable.ts +74 -15
- package/src/basic/effect.ts +85 -77
- package/src/basic/index.ts +7 -8
- package/src/basic/observable.ts +94 -36
- package/src/basic/signal.ts +133 -105
- package/src/basic/state.ts +46 -25
- package/src/basic/trackingContext.ts +45 -0
- package/src/creators.ts +297 -54
- package/src/index.ts +96 -43
- package/src/solid/converters.ts +186 -67
- package/src/solid/index.ts +8 -2
- package/src/solid/primitives.ts +167 -65
- package/test/array.test.ts +592 -612
- package/test/constant.test.ts +31 -33
- package/test/derivation.test.ts +531 -536
- package/test/effect.test.ts +21 -21
- package/test/map.test.ts +233 -137
- package/test/resource.test.ts +119 -121
- package/test/resourceAsync.test.ts +98 -100
- package/test/signal.test.ts +51 -55
- package/test/state.test.ts +186 -168
- package/test/stream.test.ts +189 -189
- package/test/streamAsync.test.ts +186 -186
- package/tsconfig.json +19 -18
- package/typedoc.json +37 -0
- package/vite.config.ts +23 -23
- package/vitest.config.ts +7 -7
- package/api/doc/index.md +0 -31
- package/api/doc/picoflow.array.md +0 -55
- package/api/doc/picoflow.constant.md +0 -55
- package/api/doc/picoflow.derivation.md +0 -55
- package/api/doc/picoflow.effect.md +0 -55
- package/api/doc/picoflow.flowarray._constructor_.md +0 -49
- package/api/doc/picoflow.flowarray._lastaction.md +0 -13
- package/api/doc/picoflow.flowarray.clear.md +0 -17
- package/api/doc/picoflow.flowarray.dispose.md +0 -55
- package/api/doc/picoflow.flowarray.get.md +0 -19
- package/api/doc/picoflow.flowarray.length.md +0 -13
- package/api/doc/picoflow.flowarray.md +0 -273
- package/api/doc/picoflow.flowarray.pop.md +0 -17
- package/api/doc/picoflow.flowarray.push.md +0 -53
- package/api/doc/picoflow.flowarray.set.md +0 -53
- package/api/doc/picoflow.flowarray.setitem.md +0 -69
- package/api/doc/picoflow.flowarray.shift.md +0 -17
- package/api/doc/picoflow.flowarray.splice.md +0 -85
- package/api/doc/picoflow.flowarray.unshift.md +0 -53
- package/api/doc/picoflow.flowarrayaction.md +0 -37
- package/api/doc/picoflow.flowconstant._constructor_.md +0 -49
- package/api/doc/picoflow.flowconstant.get.md +0 -25
- package/api/doc/picoflow.flowconstant.md +0 -88
- package/api/doc/picoflow.flowderivation._constructor_.md +0 -49
- package/api/doc/picoflow.flowderivation.get.md +0 -23
- package/api/doc/picoflow.flowderivation.md +0 -86
- package/api/doc/picoflow.flowdisposable.dispose.md +0 -55
- package/api/doc/picoflow.flowdisposable.md +0 -43
- package/api/doc/picoflow.floweffect._constructor_.md +0 -54
- package/api/doc/picoflow.floweffect.dispose.md +0 -21
- package/api/doc/picoflow.floweffect.disposed.md +0 -13
- package/api/doc/picoflow.floweffect.md +0 -131
- package/api/doc/picoflow.flowgetter.md +0 -15
- package/api/doc/picoflow.flowmap._lastdeleted.md +0 -21
- package/api/doc/picoflow.flowmap._lastset.md +0 -21
- package/api/doc/picoflow.flowmap.delete.md +0 -61
- package/api/doc/picoflow.flowmap.md +0 -133
- package/api/doc/picoflow.flowmap.setat.md +0 -77
- package/api/doc/picoflow.flowobservable.get.md +0 -19
- package/api/doc/picoflow.flowobservable.md +0 -68
- package/api/doc/picoflow.flowobservable.subscribe.md +0 -55
- package/api/doc/picoflow.flowresource._constructor_.md +0 -49
- package/api/doc/picoflow.flowresource.fetch.md +0 -27
- package/api/doc/picoflow.flowresource.get.md +0 -23
- package/api/doc/picoflow.flowresource.md +0 -100
- package/api/doc/picoflow.flowresourceasync._constructor_.md +0 -49
- package/api/doc/picoflow.flowresourceasync.fetch.md +0 -27
- package/api/doc/picoflow.flowresourceasync.get.md +0 -23
- package/api/doc/picoflow.flowresourceasync.md +0 -100
- package/api/doc/picoflow.flowsignal.dispose.md +0 -59
- package/api/doc/picoflow.flowsignal.disposed.md +0 -18
- package/api/doc/picoflow.flowsignal.md +0 -112
- package/api/doc/picoflow.flowsignal.trigger.md +0 -21
- package/api/doc/picoflow.flowstate.md +0 -52
- package/api/doc/picoflow.flowstate.set.md +0 -61
- package/api/doc/picoflow.flowstream._constructor_.md +0 -49
- package/api/doc/picoflow.flowstream.dispose.md +0 -21
- package/api/doc/picoflow.flowstream.get.md +0 -23
- package/api/doc/picoflow.flowstream.md +0 -100
- package/api/doc/picoflow.flowstreamasync._constructor_.md +0 -54
- package/api/doc/picoflow.flowstreamasync.dispose.md +0 -21
- package/api/doc/picoflow.flowstreamasync.get.md +0 -23
- package/api/doc/picoflow.flowstreamasync.md +0 -100
- package/api/doc/picoflow.flowstreamdisposer.md +0 -13
- package/api/doc/picoflow.flowstreamsetter.md +0 -13
- package/api/doc/picoflow.flowstreamupdater.md +0 -19
- package/api/doc/picoflow.flowwatcher.md +0 -15
- package/api/doc/picoflow.from.md +0 -55
- package/api/doc/picoflow.from_1.md +0 -55
- package/api/doc/picoflow.from_2.md +0 -55
- package/api/doc/picoflow.from_3.md +0 -55
- package/api/doc/picoflow.from_4.md +0 -55
- package/api/doc/picoflow.from_5.md +0 -55
- package/api/doc/picoflow.isdisposable.md +0 -55
- package/api/doc/picoflow.map.md +0 -59
- package/api/doc/picoflow.md +0 -544
- package/api/doc/picoflow.resource.md +0 -55
- package/api/doc/picoflow.resourceasync.md +0 -55
- package/api/doc/picoflow.signal.md +0 -19
- package/api/doc/picoflow.solidderivation._constructor_.md +0 -49
- package/api/doc/picoflow.solidderivation.get.md +0 -13
- package/api/doc/picoflow.solidderivation.md +0 -94
- package/api/doc/picoflow.solidgetter.md +0 -13
- package/api/doc/picoflow.solidobservable.get.md +0 -13
- package/api/doc/picoflow.solidobservable.md +0 -57
- package/api/doc/picoflow.solidresource._constructor_.md +0 -49
- package/api/doc/picoflow.solidresource.get.md +0 -13
- package/api/doc/picoflow.solidresource.latest.md +0 -13
- package/api/doc/picoflow.solidresource.md +0 -157
- package/api/doc/picoflow.solidresource.refetch.md +0 -13
- package/api/doc/picoflow.solidresource.state.md +0 -13
- package/api/doc/picoflow.solidstate._constructor_.md +0 -49
- package/api/doc/picoflow.solidstate.get.md +0 -13
- package/api/doc/picoflow.solidstate.md +0 -115
- package/api/doc/picoflow.solidstate.set.md +0 -13
- package/api/doc/picoflow.state.md +0 -55
- package/api/doc/picoflow.stream.md +0 -55
- package/api/doc/picoflow.streamasync.md +0 -55
- package/api/picoflow.public.api.md +0 -244
- package/api-extractor.json +0 -61
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# Arrays
|
|
2
|
+
|
|
3
|
+
PicoFlow provides reactive arrays with **fine-grained tracking**. Instead of reacting to the entire array changing, you can react to specific operations like pushing, popping, or splicing individual items.
|
|
4
|
+
|
|
5
|
+
Imagine you have a list of 1000 todos. In a naive reactive system, every time you add, remove, or update a single todo, the entire list re-renders. **Inefficient!** With PicoFlow's reactive arrays, you can track specific operations and only update what changed.
|
|
6
|
+
|
|
7
|
+
### Key Characteristics
|
|
8
|
+
|
|
9
|
+
- **Fine-grained reactivity**: Track individual operations (push, pop, splice, etc.) separately
|
|
10
|
+
- **Multiple tracking levels**: Track the whole array or specific operations
|
|
11
|
+
- **Native Array**: Uses JavaScript's native `Array<T>` internally
|
|
12
|
+
- **Operation-specific signal**: `$lastAction` for granular tracking of all operations
|
|
13
|
+
- **Disposable**: Can be disposed to clean up resources
|
|
14
|
+
|
|
15
|
+
## When to Use Arrays
|
|
16
|
+
|
|
17
|
+
Use arrays when you need to:
|
|
18
|
+
|
|
19
|
+
- ✅ Track large collections with fine-grained updates
|
|
20
|
+
- ✅ React to specific operations (additions, removals, updates) separately
|
|
21
|
+
- ✅ Manage ordered collections where individual item operations matter
|
|
22
|
+
- ✅ Optimize performance by avoiding full array re-processing
|
|
23
|
+
- ✅ Animate or handle UI updates for specific changes
|
|
24
|
+
|
|
25
|
+
Don't use arrays when:
|
|
26
|
+
|
|
27
|
+
- ❌ You have a small, simple list (use `state` instead)
|
|
28
|
+
- ❌ You don't need fine-grained tracking (use `state` with an array)
|
|
29
|
+
- ❌ You need key-value operations (use `map` instead)
|
|
30
|
+
- ❌ The collection is rarely modified (regular state might be simpler)
|
|
31
|
+
|
|
32
|
+
## Creating Arrays
|
|
33
|
+
|
|
34
|
+
Creating a reactive array is straightforward:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { array } from '@ersbeth/picoflow'
|
|
38
|
+
|
|
39
|
+
// Create with initial values
|
|
40
|
+
const $todos = array([
|
|
41
|
+
{ id: 1, text: 'Learn PicoFlow', done: false },
|
|
42
|
+
{ id: 2, text: 'Build app', done: false }
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
// Create empty
|
|
46
|
+
const $items = array<string>()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Using Arrays
|
|
50
|
+
|
|
51
|
+
Arrays provide multiple ways to track changes and perform operations.
|
|
52
|
+
|
|
53
|
+
### Array Operations
|
|
54
|
+
|
|
55
|
+
Arrays provide several operations: `set()`, `setItem()`, `push()`, `pop()`, `unshift()`, `shift()`, `splice()`, and `clear()`.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
const $list = array<number>([])
|
|
59
|
+
|
|
60
|
+
// Replace entire array
|
|
61
|
+
$list.set([1, 2, 3])
|
|
62
|
+
|
|
63
|
+
// Replace item at index
|
|
64
|
+
$list.setItem(0, 10)
|
|
65
|
+
|
|
66
|
+
// Add to end
|
|
67
|
+
$list.push(4)
|
|
68
|
+
|
|
69
|
+
// Add to start
|
|
70
|
+
$list.unshift(0)
|
|
71
|
+
|
|
72
|
+
// Remove from end
|
|
73
|
+
$list.pop()
|
|
74
|
+
|
|
75
|
+
// Remove from start
|
|
76
|
+
$list.shift()
|
|
77
|
+
|
|
78
|
+
// Splice (remove/add at index)
|
|
79
|
+
$list.splice(1, 1, 99) // Remove 1 item at index 1, add 99
|
|
80
|
+
|
|
81
|
+
// Clear all
|
|
82
|
+
$list.clear()
|
|
83
|
+
|
|
84
|
+
// Read (reactive)
|
|
85
|
+
effect((t) => {
|
|
86
|
+
const items = $list.get(t)
|
|
87
|
+
// Track entire array
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// Read (non-reactive)
|
|
91
|
+
const snapshot = $list.pick()
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Important**:
|
|
95
|
+
- `setItem(index, item)` throws an error if the index is out of bounds
|
|
96
|
+
- `push(item)` only accepts a single item
|
|
97
|
+
- `splice(start, deleteCount, ...items)` accepts multiple items to add
|
|
98
|
+
|
|
99
|
+
### Whole Array Tracking with `.get(t)`
|
|
100
|
+
|
|
101
|
+
Track when the entire array changes (any operation):
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { array, effect } from '@ersbeth/picoflow'
|
|
105
|
+
|
|
106
|
+
const $items = array([1, 2, 3])
|
|
107
|
+
|
|
108
|
+
effect((t) => {
|
|
109
|
+
const items = $items.get(t) // Track all changes
|
|
110
|
+
console.log('Array changed:', items)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
$items.push(4) // Logs: "Array changed: [1,2,3,4]"
|
|
114
|
+
$items.pop() // Logs: "Array changed: [1,2,3]"
|
|
115
|
+
$items.splice(1, 1) // Logs: "Array changed: [1,3]"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Use `.get(t)` when you need the entire array's current state. It returns a copy of the array.
|
|
119
|
+
|
|
120
|
+
### Fine-Grained: Track Operations
|
|
121
|
+
|
|
122
|
+
Track specific operations with `$lastAction`:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const $items = array<number>([])
|
|
126
|
+
|
|
127
|
+
effect((t) => {
|
|
128
|
+
const action = $items.$lastAction.get(t)
|
|
129
|
+
if (!action) return
|
|
130
|
+
|
|
131
|
+
switch (action.type) {
|
|
132
|
+
case 'push':
|
|
133
|
+
console.log('Pushed:', action.item)
|
|
134
|
+
break
|
|
135
|
+
case 'pop':
|
|
136
|
+
console.log('Popped')
|
|
137
|
+
break
|
|
138
|
+
case 'splice':
|
|
139
|
+
console.log('Splice at', action.start, ':', action.deleteCount, 'deleted,', action.items.length, 'added')
|
|
140
|
+
break
|
|
141
|
+
case 'setItem':
|
|
142
|
+
console.log('Set item at', action.index, 'to', action.item)
|
|
143
|
+
break
|
|
144
|
+
// ... other operations
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
$items.push(1) // Logs: "Pushed: 1"
|
|
149
|
+
$items.push(2) // Logs: "Pushed: 2"
|
|
150
|
+
$items.pop() // Logs: "Popped"
|
|
151
|
+
$items.splice(0, 1) // Logs: "Splice at 0: 1 deleted, 0 added"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Array Action Types
|
|
155
|
+
|
|
156
|
+
The `$lastAction` signal contains information about the last operation:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
type FlowArrayAction<T> =
|
|
160
|
+
| { type: 'set'; items: T[] }
|
|
161
|
+
| { type: 'setItem'; index: number; item: T }
|
|
162
|
+
| { type: 'push'; item: T }
|
|
163
|
+
| { type: 'pop' }
|
|
164
|
+
| { type: 'shift' }
|
|
165
|
+
| { type: 'unshift'; item: T }
|
|
166
|
+
| { type: 'splice'; start: number; deleteCount: number; items: T[] }
|
|
167
|
+
| { type: 'clear' }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Lifecycle
|
|
171
|
+
|
|
172
|
+
When you create an effect that tracks an array:
|
|
173
|
+
|
|
174
|
+
1. **Registration**: The array registers the effect as a watcher
|
|
175
|
+
2. **Operation**: When you call `push()`, `pop()`, `splice()`, etc., the array updates internally
|
|
176
|
+
3. **Signal Update**: The `$lastAction` signal is updated with the operation details
|
|
177
|
+
4. **Notification**: All watching effects are scheduled to run
|
|
178
|
+
5. **Re-execution**: Each watching effect re-executes its function
|
|
179
|
+
|
|
180
|
+
```mermaid
|
|
181
|
+
sequenceDiagram
|
|
182
|
+
participant User
|
|
183
|
+
participant Array as $todos (Array)
|
|
184
|
+
participant Signal as $lastAction
|
|
185
|
+
participant Effect
|
|
186
|
+
|
|
187
|
+
Note over User,Effect: 1. Setup Phase
|
|
188
|
+
User->>Effect: Create effect
|
|
189
|
+
activate Effect
|
|
190
|
+
Effect->>Array: get(t)
|
|
191
|
+
Note over Array: Register Effect as watcher
|
|
192
|
+
Effect->>Signal: get(t)
|
|
193
|
+
Note over Signal: Register Effect as watcher
|
|
194
|
+
Effect->>Effect: Execute function
|
|
195
|
+
Note over Effect: Initial render
|
|
196
|
+
deactivate Effect
|
|
197
|
+
|
|
198
|
+
Note over User,Effect: 2. Operation Phase
|
|
199
|
+
User->>Array: push(todo)
|
|
200
|
+
activate Array
|
|
201
|
+
Note over Array: Update internal array
|
|
202
|
+
Array->>Signal: set({ type: 'push', item: todo })
|
|
203
|
+
activate Signal
|
|
204
|
+
Note over Signal: Notify watchers
|
|
205
|
+
Signal->>Effect: Schedule execution
|
|
206
|
+
deactivate Signal
|
|
207
|
+
Array->>Array: Notify whole array watchers
|
|
208
|
+
Array->>Effect: Schedule execution
|
|
209
|
+
deactivate Array
|
|
210
|
+
|
|
211
|
+
activate Effect
|
|
212
|
+
Note over Effect: Re-execute function
|
|
213
|
+
Effect->>Signal: get(t)
|
|
214
|
+
Signal-->>Effect: { type: 'push', item: todo }
|
|
215
|
+
Effect->>Effect: Handle new todo
|
|
216
|
+
Note over Effect: Update UI
|
|
217
|
+
deactivate Effect
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Best Practices
|
|
221
|
+
|
|
222
|
+
### Choose the Right Granularity
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// ✅ Use fine-grained for large arrays
|
|
226
|
+
const $todos = array<Todo>([...1000 todos...])
|
|
227
|
+
effect((t) => {
|
|
228
|
+
const action = $todos.$lastAction.get(t)
|
|
229
|
+
if (action) {
|
|
230
|
+
// Handle specific change
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
// ✅ Use coarse-grained for small arrays
|
|
235
|
+
const $settings = state(['dark', 'en', 'large'])
|
|
236
|
+
effect((t) => {
|
|
237
|
+
const settings = $settings.get(t)
|
|
238
|
+
// Re-apply all settings (cheap for 3 items)
|
|
239
|
+
})
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Combine for Best Results
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
// Fine-grained for UI updates
|
|
246
|
+
effect((t) => {
|
|
247
|
+
const action = $items.$lastAction.get(t)
|
|
248
|
+
if (action) {
|
|
249
|
+
updateUIIncremental(action)
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
// Coarse-grained for totals
|
|
254
|
+
const $total = derivation((t) => {
|
|
255
|
+
return $items.get(t).reduce((sum, item) => sum + item.value, 0)
|
|
256
|
+
})
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Handle All Action Types
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
effect((t) => {
|
|
263
|
+
const action = $items.$lastAction.get(t)
|
|
264
|
+
if (!action) return
|
|
265
|
+
|
|
266
|
+
switch (action.type) {
|
|
267
|
+
case 'push':
|
|
268
|
+
// Handle push
|
|
269
|
+
break
|
|
270
|
+
case 'pop':
|
|
271
|
+
// Handle pop
|
|
272
|
+
break
|
|
273
|
+
case 'splice':
|
|
274
|
+
// Handle splice
|
|
275
|
+
break
|
|
276
|
+
case 'setItem':
|
|
277
|
+
// Handle setItem
|
|
278
|
+
break
|
|
279
|
+
case 'clear':
|
|
280
|
+
// Handle clear
|
|
281
|
+
break
|
|
282
|
+
// Don't forget any cases!
|
|
283
|
+
}
|
|
284
|
+
})
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Dispose Items Properly
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
// When removing disposable items
|
|
291
|
+
effect((t) => {
|
|
292
|
+
const action = $resources.$lastAction.get(t)
|
|
293
|
+
if (action && action.type === 'splice') {
|
|
294
|
+
// Dispose removed items (they're already disposed by the array)
|
|
295
|
+
// But you might want to do additional cleanup
|
|
296
|
+
}
|
|
297
|
+
})
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Arrays automatically dispose disposable items when they are removed, but you can add additional cleanup logic if needed.
|
|
301
|
+
|
|
302
|
+
## Common Pitfalls
|
|
303
|
+
|
|
304
|
+
### Forgetting to Handle Nulls
|
|
305
|
+
|
|
306
|
+
**Problem**: The `$lastAction` signal can be `null` initially or after certain operations.
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
// ❌ Can be null initially
|
|
310
|
+
effect((t) => {
|
|
311
|
+
const action = $items.$lastAction.get(t)
|
|
312
|
+
console.log(action.type) // Error if null!
|
|
313
|
+
})
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Solution**: Always check for null:
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
// ✅ Check for null
|
|
320
|
+
effect((t) => {
|
|
321
|
+
const action = $items.$lastAction.get(t)
|
|
322
|
+
if (action) {
|
|
323
|
+
console.log(action.type)
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Using Wrong Tracking
|
|
329
|
+
|
|
330
|
+
**Problem**: Tracking the entire array when you only need specific operations.
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
// ❌ Tracks entire array when you only need additions
|
|
334
|
+
effect((t) => {
|
|
335
|
+
const items = $items.get(t) // Runs on ALL changes
|
|
336
|
+
const lastItem = items[items.length - 1]
|
|
337
|
+
animateNewItem(lastItem)
|
|
338
|
+
})
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Solution**: Track only what you need:
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
// ✅ Track only pushes
|
|
345
|
+
effect((t) => {
|
|
346
|
+
const action = $items.$lastAction.get(t)
|
|
347
|
+
if (action && action.type === 'push') {
|
|
348
|
+
animateNewItem(action.item)
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Mutating Retrieved Arrays
|
|
354
|
+
|
|
355
|
+
**Problem**: Mutating the array directly instead of using reactive methods.
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
// ❌ Mutating the array
|
|
359
|
+
const items = $items.pick()
|
|
360
|
+
items.push(newItem) // Doesn't update reactive array!
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**Solution**: Use reactive methods:
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// ✅ Use reactive methods
|
|
367
|
+
$items.push(newItem)
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Using Wrong Action Properties
|
|
371
|
+
|
|
372
|
+
**Problem**: Using incorrect property names from action objects.
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
// ❌ Wrong property names
|
|
376
|
+
effect((t) => {
|
|
377
|
+
const action = $items.$lastAction.get(t)
|
|
378
|
+
if (action && action.type === 'push') {
|
|
379
|
+
console.log(action.value) // Error! Should be action.item
|
|
380
|
+
}
|
|
381
|
+
if (action && action.type === 'splice') {
|
|
382
|
+
console.log(action.index) // Error! Should be action.start
|
|
383
|
+
}
|
|
384
|
+
})
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**Solution**: Use correct property names:
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
// ✅ Correct property names
|
|
391
|
+
effect((t) => {
|
|
392
|
+
const action = $items.$lastAction.get(t)
|
|
393
|
+
if (action && action.type === 'push') {
|
|
394
|
+
console.log(action.item) // Correct
|
|
395
|
+
}
|
|
396
|
+
if (action && action.type === 'splice') {
|
|
397
|
+
console.log(action.start) // Correct
|
|
398
|
+
}
|
|
399
|
+
})
|
|
400
|
+
```
|