@ersbeth/picoflow 0.2.3 → 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.
Files changed (248) hide show
  1. package/.cursor/plans/update-js-e795d61b.plan.md +567 -0
  2. package/.gitlab-ci.yml +24 -0
  3. package/.vscode/settings.json +3 -3
  4. package/CHANGELOG.md +51 -0
  5. package/IMPLEMENTATION_GUIDE.md +1578 -0
  6. package/README.md +62 -25
  7. package/biome.json +32 -32
  8. package/dist/picoflow.js +557 -1099
  9. package/dist/types/advanced/array.d.ts +0 -6
  10. package/dist/types/advanced/array.d.ts.map +1 -1
  11. package/dist/types/advanced/index.d.ts +5 -5
  12. package/dist/types/advanced/index.d.ts.map +1 -1
  13. package/dist/types/advanced/map.d.ts +114 -23
  14. package/dist/types/advanced/map.d.ts.map +1 -1
  15. package/dist/types/advanced/resource.d.ts +51 -12
  16. package/dist/types/advanced/resource.d.ts.map +1 -1
  17. package/dist/types/advanced/resourceAsync.d.ts +28 -13
  18. package/dist/types/advanced/resourceAsync.d.ts.map +1 -1
  19. package/dist/types/advanced/stream.d.ts +74 -16
  20. package/dist/types/advanced/stream.d.ts.map +1 -1
  21. package/dist/types/advanced/streamAsync.d.ts +69 -15
  22. package/dist/types/advanced/streamAsync.d.ts.map +1 -1
  23. package/dist/types/basic/constant.d.ts +44 -16
  24. package/dist/types/basic/constant.d.ts.map +1 -1
  25. package/dist/types/basic/derivation.d.ts +73 -24
  26. package/dist/types/basic/derivation.d.ts.map +1 -1
  27. package/dist/types/basic/disposable.d.ts +65 -6
  28. package/dist/types/basic/disposable.d.ts.map +1 -1
  29. package/dist/types/basic/effect.d.ts +27 -16
  30. package/dist/types/basic/effect.d.ts.map +1 -1
  31. package/dist/types/basic/index.d.ts +7 -8
  32. package/dist/types/basic/index.d.ts.map +1 -1
  33. package/dist/types/basic/observable.d.ts +62 -13
  34. package/dist/types/basic/observable.d.ts.map +1 -1
  35. package/dist/types/basic/signal.d.ts +35 -6
  36. package/dist/types/basic/signal.d.ts.map +1 -1
  37. package/dist/types/basic/state.d.ts +25 -4
  38. package/dist/types/basic/state.d.ts.map +1 -1
  39. package/dist/types/basic/trackingContext.d.ts +33 -0
  40. package/dist/types/basic/trackingContext.d.ts.map +1 -0
  41. package/dist/types/creators.d.ts +271 -26
  42. package/dist/types/creators.d.ts.map +1 -1
  43. package/dist/types/index.d.ts +60 -7
  44. package/dist/types/index.d.ts.map +1 -1
  45. package/dist/types/solid/converters.d.ts +5 -5
  46. package/dist/types/solid/converters.d.ts.map +1 -1
  47. package/dist/types/solid/index.d.ts +2 -2
  48. package/dist/types/solid/index.d.ts.map +1 -1
  49. package/dist/types/solid/primitives.d.ts +96 -4
  50. package/dist/types/solid/primitives.d.ts.map +1 -1
  51. package/docs/.vitepress/config.mts +110 -0
  52. package/docs/api/classes/FlowArray.md +489 -0
  53. package/docs/api/classes/FlowConstant.md +350 -0
  54. package/docs/api/classes/FlowDerivation.md +334 -0
  55. package/docs/api/classes/FlowEffect.md +100 -0
  56. package/docs/api/classes/FlowMap.md +512 -0
  57. package/docs/api/classes/FlowObservable.md +306 -0
  58. package/docs/api/classes/FlowResource.md +380 -0
  59. package/docs/api/classes/FlowResourceAsync.md +362 -0
  60. package/docs/api/classes/FlowSignal.md +160 -0
  61. package/docs/api/classes/FlowState.md +368 -0
  62. package/docs/api/classes/FlowStream.md +367 -0
  63. package/docs/api/classes/FlowStreamAsync.md +364 -0
  64. package/docs/api/classes/SolidDerivation.md +75 -0
  65. package/docs/api/classes/SolidResource.md +91 -0
  66. package/docs/api/classes/SolidState.md +71 -0
  67. package/docs/api/classes/TrackingContext.md +33 -0
  68. package/docs/api/functions/array.md +58 -0
  69. package/docs/api/functions/constant.md +45 -0
  70. package/docs/api/functions/derivation.md +53 -0
  71. package/docs/api/functions/effect.md +49 -0
  72. package/docs/api/functions/from.md +220 -0
  73. package/docs/api/functions/isDisposable.md +49 -0
  74. package/docs/api/functions/map.md +57 -0
  75. package/docs/api/functions/resource.md +52 -0
  76. package/docs/api/functions/resourceAsync.md +50 -0
  77. package/docs/api/functions/signal.md +36 -0
  78. package/docs/api/functions/state.md +47 -0
  79. package/docs/api/functions/stream.md +53 -0
  80. package/docs/api/functions/streamAsync.md +50 -0
  81. package/docs/api/index.md +118 -0
  82. package/docs/api/interfaces/FlowDisposable.md +65 -0
  83. package/docs/api/interfaces/SolidObservable.md +19 -0
  84. package/docs/api/type-aliases/FlowArrayAction.md +49 -0
  85. package/docs/api/type-aliases/FlowStreamDisposer.md +15 -0
  86. package/docs/api/type-aliases/FlowStreamSetter.md +27 -0
  87. package/docs/api/type-aliases/FlowStreamUpdater.md +32 -0
  88. package/docs/api/type-aliases/NotPromise.md +18 -0
  89. package/docs/api/type-aliases/SolidGetter.md +17 -0
  90. package/docs/api/typedoc-sidebar.json +1 -0
  91. package/docs/examples/examples.md +2313 -0
  92. package/docs/examples/patterns.md +649 -0
  93. package/docs/guide/advanced/disposal.md +426 -0
  94. package/docs/guide/advanced/solidjs.md +221 -0
  95. package/docs/guide/advanced/upgrading.md +464 -0
  96. package/docs/guide/introduction/concepts.md +56 -0
  97. package/docs/guide/introduction/conventions.md +61 -0
  98. package/docs/guide/introduction/getting-started.md +134 -0
  99. package/docs/guide/introduction/lifecycle.md +371 -0
  100. package/docs/guide/primitives/array.md +400 -0
  101. package/docs/guide/primitives/constant.md +380 -0
  102. package/docs/guide/primitives/derivations.md +348 -0
  103. package/docs/guide/primitives/effects.md +458 -0
  104. package/docs/guide/primitives/map.md +387 -0
  105. package/docs/guide/primitives/overview.md +175 -0
  106. package/docs/guide/primitives/resources.md +858 -0
  107. package/docs/guide/primitives/signal.md +259 -0
  108. package/docs/guide/primitives/state.md +368 -0
  109. package/docs/guide/primitives/streams.md +931 -0
  110. package/docs/index.md +47 -0
  111. package/docs/public/logo.svg +1 -0
  112. package/package.json +57 -41
  113. package/src/advanced/array.ts +208 -210
  114. package/src/advanced/index.ts +7 -7
  115. package/src/advanced/map.ts +178 -68
  116. package/src/advanced/resource.ts +87 -43
  117. package/src/advanced/resourceAsync.ts +62 -42
  118. package/src/advanced/stream.ts +113 -50
  119. package/src/advanced/streamAsync.ts +120 -61
  120. package/src/basic/constant.ts +82 -49
  121. package/src/basic/derivation.ts +128 -84
  122. package/src/basic/disposable.ts +74 -15
  123. package/src/basic/effect.ts +85 -77
  124. package/src/basic/index.ts +7 -8
  125. package/src/basic/observable.ts +94 -36
  126. package/src/basic/signal.ts +133 -105
  127. package/src/basic/state.ts +46 -25
  128. package/src/basic/trackingContext.ts +45 -0
  129. package/src/creators.ts +297 -54
  130. package/src/index.ts +96 -43
  131. package/src/solid/converters.ts +186 -67
  132. package/src/solid/index.ts +8 -2
  133. package/src/solid/primitives.ts +167 -65
  134. package/test/array.test.ts +592 -612
  135. package/test/constant.test.ts +31 -33
  136. package/test/derivation.test.ts +531 -536
  137. package/test/effect.test.ts +21 -21
  138. package/test/map.test.ts +233 -137
  139. package/test/resource.test.ts +119 -121
  140. package/test/resourceAsync.test.ts +98 -100
  141. package/test/signal.test.ts +51 -55
  142. package/test/state.test.ts +186 -168
  143. package/test/stream.test.ts +189 -189
  144. package/test/streamAsync.test.ts +186 -186
  145. package/tsconfig.json +19 -18
  146. package/typedoc.json +37 -0
  147. package/vite.config.ts +23 -20
  148. package/vitest.config.ts +7 -7
  149. package/api/doc/index.md +0 -31
  150. package/api/doc/picoflow.array.md +0 -55
  151. package/api/doc/picoflow.constant.md +0 -55
  152. package/api/doc/picoflow.derivation.md +0 -55
  153. package/api/doc/picoflow.effect.md +0 -55
  154. package/api/doc/picoflow.flowarray._constructor_.md +0 -49
  155. package/api/doc/picoflow.flowarray._lastaction.md +0 -13
  156. package/api/doc/picoflow.flowarray.clear.md +0 -17
  157. package/api/doc/picoflow.flowarray.dispose.md +0 -55
  158. package/api/doc/picoflow.flowarray.get.md +0 -19
  159. package/api/doc/picoflow.flowarray.length.md +0 -13
  160. package/api/doc/picoflow.flowarray.md +0 -273
  161. package/api/doc/picoflow.flowarray.pop.md +0 -17
  162. package/api/doc/picoflow.flowarray.push.md +0 -53
  163. package/api/doc/picoflow.flowarray.set.md +0 -53
  164. package/api/doc/picoflow.flowarray.setitem.md +0 -69
  165. package/api/doc/picoflow.flowarray.shift.md +0 -17
  166. package/api/doc/picoflow.flowarray.splice.md +0 -85
  167. package/api/doc/picoflow.flowarray.unshift.md +0 -53
  168. package/api/doc/picoflow.flowarrayaction.md +0 -37
  169. package/api/doc/picoflow.flowconstant._constructor_.md +0 -49
  170. package/api/doc/picoflow.flowconstant.get.md +0 -25
  171. package/api/doc/picoflow.flowconstant.md +0 -88
  172. package/api/doc/picoflow.flowderivation._constructor_.md +0 -49
  173. package/api/doc/picoflow.flowderivation.get.md +0 -23
  174. package/api/doc/picoflow.flowderivation.md +0 -86
  175. package/api/doc/picoflow.flowdisposable.dispose.md +0 -55
  176. package/api/doc/picoflow.flowdisposable.md +0 -43
  177. package/api/doc/picoflow.floweffect._constructor_.md +0 -54
  178. package/api/doc/picoflow.floweffect.dispose.md +0 -21
  179. package/api/doc/picoflow.floweffect.disposed.md +0 -13
  180. package/api/doc/picoflow.floweffect.md +0 -131
  181. package/api/doc/picoflow.flowgetter.md +0 -15
  182. package/api/doc/picoflow.flowmap._lastdeleted.md +0 -21
  183. package/api/doc/picoflow.flowmap._lastset.md +0 -21
  184. package/api/doc/picoflow.flowmap.delete.md +0 -61
  185. package/api/doc/picoflow.flowmap.md +0 -133
  186. package/api/doc/picoflow.flowmap.setat.md +0 -77
  187. package/api/doc/picoflow.flowobservable.get.md +0 -19
  188. package/api/doc/picoflow.flowobservable.md +0 -68
  189. package/api/doc/picoflow.flowobservable.subscribe.md +0 -55
  190. package/api/doc/picoflow.flowresource._constructor_.md +0 -49
  191. package/api/doc/picoflow.flowresource.fetch.md +0 -27
  192. package/api/doc/picoflow.flowresource.get.md +0 -23
  193. package/api/doc/picoflow.flowresource.md +0 -100
  194. package/api/doc/picoflow.flowresourceasync._constructor_.md +0 -49
  195. package/api/doc/picoflow.flowresourceasync.fetch.md +0 -27
  196. package/api/doc/picoflow.flowresourceasync.get.md +0 -23
  197. package/api/doc/picoflow.flowresourceasync.md +0 -100
  198. package/api/doc/picoflow.flowsignal.dispose.md +0 -59
  199. package/api/doc/picoflow.flowsignal.disposed.md +0 -18
  200. package/api/doc/picoflow.flowsignal.md +0 -112
  201. package/api/doc/picoflow.flowsignal.trigger.md +0 -21
  202. package/api/doc/picoflow.flowstate.md +0 -52
  203. package/api/doc/picoflow.flowstate.set.md +0 -61
  204. package/api/doc/picoflow.flowstream._constructor_.md +0 -49
  205. package/api/doc/picoflow.flowstream.dispose.md +0 -21
  206. package/api/doc/picoflow.flowstream.get.md +0 -23
  207. package/api/doc/picoflow.flowstream.md +0 -100
  208. package/api/doc/picoflow.flowstreamasync._constructor_.md +0 -54
  209. package/api/doc/picoflow.flowstreamasync.dispose.md +0 -21
  210. package/api/doc/picoflow.flowstreamasync.get.md +0 -23
  211. package/api/doc/picoflow.flowstreamasync.md +0 -100
  212. package/api/doc/picoflow.flowstreamdisposer.md +0 -13
  213. package/api/doc/picoflow.flowstreamsetter.md +0 -13
  214. package/api/doc/picoflow.flowstreamupdater.md +0 -19
  215. package/api/doc/picoflow.flowwatcher.md +0 -15
  216. package/api/doc/picoflow.from.md +0 -55
  217. package/api/doc/picoflow.from_1.md +0 -55
  218. package/api/doc/picoflow.from_2.md +0 -55
  219. package/api/doc/picoflow.from_3.md +0 -55
  220. package/api/doc/picoflow.from_4.md +0 -55
  221. package/api/doc/picoflow.from_5.md +0 -55
  222. package/api/doc/picoflow.isdisposable.md +0 -55
  223. package/api/doc/picoflow.map.md +0 -59
  224. package/api/doc/picoflow.md +0 -544
  225. package/api/doc/picoflow.resource.md +0 -55
  226. package/api/doc/picoflow.resourceasync.md +0 -55
  227. package/api/doc/picoflow.signal.md +0 -19
  228. package/api/doc/picoflow.solidderivation._constructor_.md +0 -49
  229. package/api/doc/picoflow.solidderivation.get.md +0 -13
  230. package/api/doc/picoflow.solidderivation.md +0 -94
  231. package/api/doc/picoflow.solidgetter.md +0 -13
  232. package/api/doc/picoflow.solidobservable.get.md +0 -13
  233. package/api/doc/picoflow.solidobservable.md +0 -57
  234. package/api/doc/picoflow.solidresource._constructor_.md +0 -49
  235. package/api/doc/picoflow.solidresource.get.md +0 -13
  236. package/api/doc/picoflow.solidresource.latest.md +0 -13
  237. package/api/doc/picoflow.solidresource.md +0 -157
  238. package/api/doc/picoflow.solidresource.refetch.md +0 -13
  239. package/api/doc/picoflow.solidresource.state.md +0 -13
  240. package/api/doc/picoflow.solidstate._constructor_.md +0 -49
  241. package/api/doc/picoflow.solidstate.get.md +0 -13
  242. package/api/doc/picoflow.solidstate.md +0 -115
  243. package/api/doc/picoflow.solidstate.set.md +0 -13
  244. package/api/doc/picoflow.state.md +0 -55
  245. package/api/doc/picoflow.stream.md +0 -55
  246. package/api/doc/picoflow.streamasync.md +0 -55
  247. package/api/picoflow.public.api.md +0 -244
  248. package/api-extractor.json +0 -61
@@ -0,0 +1,458 @@
1
+ # Effects
2
+
3
+ Effects are how you perform **side effects** in response to reactive changes. They're the bridge between your reactive data and the outside world - updating the DOM, making API calls, saving to localStorage, and more.
4
+
5
+ Imagine a light switch: when you flip it, the light turns on. Effects work the same way in reactive programming - when your data changes, effects automatically run to update the world around them.
6
+
7
+ ### Key Characteristics
8
+
9
+ - **Immediate execution**: Runs synchronously when created
10
+ - **Automatic re-execution**: Re-runs when dependencies change
11
+ - **Dynamic dependency tracking**: Dependencies adapt at runtime
12
+ - **Side effects allowed**: Designed for DOM, API, logging, etc.
13
+ - **Disposal support**: Clean up resources when no longer needed
14
+
15
+ ## When to Use Effects
16
+
17
+ Use effects when you need to:
18
+
19
+ - ✅ Perform side effects (DOM updates, API calls, localStorage)
20
+ - ✅ React to changes in reactive values
21
+ - ✅ Coordinate with external systems
22
+ - ✅ Set up subscriptions or event listeners
23
+ - ✅ Log or debug reactive changes
24
+
25
+ Don't use effects when:
26
+
27
+ - ❌ Computing derived values (use derivations instead)
28
+ - ❌ The value needs to be used elsewhere reactively (use derivations)
29
+ - ❌ You only need to run code once (use regular functions)
30
+
31
+ ## Creating Effects
32
+
33
+ Creating an effect is straightforward:
34
+
35
+ ```typescript
36
+ import { effect } from '@ersbeth/picoflow'
37
+
38
+ effect((t) => {
39
+ // Your side effect code here
40
+ // Use t to track dependencies
41
+ })
42
+ ```
43
+
44
+ The parameter `t` is the **TrackingContext** - your tool for creating dependencies.
45
+
46
+ ## Using Effects
47
+
48
+ Effects have several ways to interact with reactive values and manage their lifecycle.
49
+
50
+ ### Tracking with `.get(t)` (Reactive)
51
+
52
+ Use `.get(t)` to create dependencies - the effect will re-run when the value changes:
53
+
54
+ ```typescript
55
+ const $username = state('Alice')
56
+
57
+ effect((t) => {
58
+ const name = $username.get(t) // Creates dependency
59
+ document.getElementById('username').textContent = name
60
+ })
61
+
62
+ $username.set('Bob') // Effect re-runs, DOM updates
63
+ ```
64
+
65
+ ### Non-Reactive Reads with `.pick()`
66
+
67
+ Use `.pick()` to read the current value without creating a dependency:
68
+
69
+ ```typescript
70
+ const $activeUserId = state(1)
71
+ const $userDatabase = state({ /* large object */ })
72
+
73
+ effect((t) => {
74
+ const activeId = $activeUserId.get(t) // Reactive
75
+ const database = $userDatabase.pick() // Snapshot
76
+
77
+ const user = database[activeId]
78
+ console.log('Active user:', user)
79
+ })
80
+
81
+ // Only changes to $activeUserId trigger the effect
82
+ // Changes to $userDatabase are ignored
83
+ ```
84
+
85
+ ### Watching Signals with `.watch(t)`
86
+
87
+ Signals are special - they don't hold values, so use `.watch(t)` to track them:
88
+
89
+ ```typescript
90
+ import { signal, effect } from '@ersbeth/picoflow'
91
+
92
+ const $refresh = signal()
93
+
94
+ effect((t) => {
95
+ $refresh.watch(t) // React to signal triggers
96
+ console.log('Refreshing data...')
97
+ fetchData()
98
+ })
99
+
100
+ $refresh.trigger() // Effect runs
101
+ ```
102
+
103
+ ### Multiple Dependencies
104
+
105
+ Effects can track multiple reactive values:
106
+
107
+ ```typescript
108
+ const $firstName = state('Alice')
109
+ const $lastName = state('Smith')
110
+
111
+ effect((t) => {
112
+ const first = $firstName.get(t)
113
+ const last = $lastName.get(t)
114
+ console.log('Full name:', first, last)
115
+ })
116
+
117
+ // Changes to EITHER trigger the effect
118
+ $firstName.set('Bob') // Effect runs
119
+ $lastName.set('Jones') // Effect runs
120
+ ```
121
+
122
+ ### Conditional Dependencies
123
+
124
+ Dependencies can be conditional - they're re-evaluated on each execution:
125
+
126
+ ```typescript
127
+ const $showDetails = state(false)
128
+ const $details = state({ info: 'secret' })
129
+
130
+ effect((t) => {
131
+ if ($showDetails.get(t)) {
132
+ // Only depends on $details when $showDetails is true
133
+ console.log('Details:', $details.get(t))
134
+ } else {
135
+ console.log('Details hidden')
136
+ }
137
+ })
138
+
139
+ // When $showDetails is false, changes to $details don't trigger the effect
140
+ $details.set({ info: 'new secret' }) // Effect doesn't run
141
+
142
+ // But once $showDetails is true...
143
+ $showDetails.set(true) // Effect runs and now tracks $details
144
+ $details.set({ info: 'updated' }) // Now this triggers the effect!
145
+ ```
146
+
147
+ ### Disposing with `.dispose()`
148
+
149
+ Effects run indefinitely unless disposed. Always clean them up when no longer needed:
150
+
151
+ ```typescript
152
+ const $count = state(0)
153
+
154
+ // Create effect
155
+ const fx = effect((t) => {
156
+ console.log($count.get(t))
157
+ })
158
+
159
+ // Later... stop the effect
160
+ fx.dispose()
161
+
162
+ // After disposal, the effect won't run anymore
163
+ $count.set(10) // No console log
164
+ ```
165
+
166
+ **Always dispose effects when:**
167
+ - Component unmounts (in UI frameworks)
168
+ - Feature is disabled
169
+ - User navigates away
170
+ - App shuts down
171
+
172
+ **Memory leak example:**
173
+
174
+ ```typescript
175
+ // ❌ Memory leak
176
+ function createUserPanel(userId: number) {
177
+ const $user = state(getUserData(userId))
178
+
179
+ effect((t) => {
180
+ updateUI($user.get(t))
181
+ })
182
+ // Effect never disposed! ⚠️
183
+ }
184
+
185
+ // Called 100 times = 100 effects still running!
186
+
187
+ // ✅ Proper cleanup
188
+ function createUserPanel(userId: number) {
189
+ const $user = state(getUserData(userId))
190
+
191
+ const fx = effect((t) => {
192
+ updateUI($user.get(t))
193
+ })
194
+
195
+ return {
196
+ dispose: () => {
197
+ fx.dispose()
198
+ $user.dispose()
199
+ }
200
+ }
201
+ }
202
+ ```
203
+
204
+ ## Lifecycle
205
+
206
+ Effects execute immediately when created, track their dependencies, and re-execute when any dependency changes. Understanding this lifecycle is key to writing efficient reactive code.
207
+
208
+ When you create an effect, it runs **synchronously** - not on the next tick, but right away. This allows effects to establish initial dependencies and set up initial side effects. When a dependency changes, the effect re-executes synchronously.
209
+
210
+ ```mermaid
211
+ sequenceDiagram
212
+ participant User
213
+ participant $count as $count (State)
214
+ participant Effect
215
+
216
+ Note over User,Effect: 1. Creation Phase
217
+ User->>Effect: Create effect((t) => ...)
218
+ activate Effect
219
+ Note over Effect: Execute immediately
220
+
221
+ Effect->>$count: get(t)
222
+ activate $count
223
+ Note over $count: Register Effect as dependent
224
+ $count-->>Effect: 0
225
+ deactivate $count
226
+
227
+ Note over Effect: console.log("Count: 0")
228
+ Note over Effect: Track: depends on $count
229
+ deactivate Effect
230
+
231
+ Note over User,Effect: 2. Change Phase
232
+ User->>$count: set(5)
233
+ activate $count
234
+ Note over $count: Value changed: 0 → 5
235
+
236
+ $count->>Effect: Notify & schedule
237
+ deactivate $count
238
+
239
+ activate Effect
240
+ Note over Effect: Clear old dependencies<br/>Execute function
241
+
242
+ Effect->>$count: get(t)
243
+ activate $count
244
+ Note over $count: Register Effect as dependent
245
+ $count-->>Effect: 5
246
+ deactivate $count
247
+
248
+ Note over Effect: console.log("Count: 5")
249
+ Note over Effect: Track: depends on $count
250
+ deactivate Effect
251
+
252
+ Note over User,Effect: 3. Disposal Phase
253
+ User->>Effect: dispose()
254
+ activate Effect
255
+ Note over Effect: Clear dependencies<br/>Unregister from $count
256
+ Note over Effect: Mark as disposed
257
+ deactivate Effect
258
+
259
+ User->>$count: set(10)
260
+ activate $count
261
+ Note over $count: Value changed<br/>No dependents to notify
262
+ deactivate $count
263
+ Note over Effect: (Does not run - disposed)
264
+ ```
265
+
266
+ ### Key Lifecycle Points
267
+
268
+ 1. **Creation & Immediate Execution**: The effect function runs synchronously during creation
269
+ 2. **Dependency Registration**: Reactive reads (`.get(t)`) register the effect as a dependent
270
+ 3. **Change Notification**: When dependencies change, the effect is scheduled to re-run
271
+ 4. **Re-execution**: Effect clears old dependencies, runs again, re-registers new dependencies
272
+ 5. **Disposal**: Clears all dependencies, unregisters from all reactive values, prevents future execution
273
+
274
+ This immediate, synchronous execution model means:
275
+ - **Effects establish dependencies on first run** (no waiting)
276
+ - **Changes trigger re-execution immediately** (synchronous)
277
+ - **Dynamic dependencies adapt automatically** (conditional tracking)
278
+
279
+ ## Best Practices
280
+
281
+ ### Keep Effects Focused
282
+
283
+ Each effect should do one thing:
284
+
285
+ ```typescript
286
+ // ❌ Doing too much
287
+ effect((t) => {
288
+ updateDOM($count.get(t))
289
+ saveToStorage($count.get(t))
290
+ sendAnalytics($count.get(t))
291
+ updateTitle($count.get(t))
292
+ })
293
+
294
+ // ✅ Separate concerns
295
+ effect((t) => updateDOM($count.get(t)))
296
+ effect((t) => saveToStorage($count.get(t)))
297
+ effect((t) => sendAnalytics($count.get(t)))
298
+ effect((t) => updateTitle($count.get(t)))
299
+ ```
300
+
301
+ **Why?** Easier to debug, test, and disable individual effects.
302
+
303
+ ### Avoid Creating Effects Inside Effects
304
+
305
+ ```typescript
306
+ // ❌ Bad - creates new effects on every run
307
+ effect((t) => {
308
+ const count = $count.get(t)
309
+ effect((t) => {
310
+ console.log('Nested:', count) // Memory leak!
311
+ })
312
+ })
313
+
314
+ // ✅ Good - create effects at the top level
315
+ effect((t) => {
316
+ const count = $count.get(t)
317
+ console.log('Count:', count)
318
+ })
319
+ ```
320
+
321
+ ### Handle Errors
322
+
323
+ Wrap risky operations in try-catch:
324
+
325
+ ```typescript
326
+ effect((t) => {
327
+ const data = $data.get(t)
328
+
329
+ try {
330
+ updateDOM(data)
331
+ } catch (error) {
332
+ console.error('Failed to update DOM:', error)
333
+ }
334
+ })
335
+ ```
336
+
337
+ ### Be Careful with Infinite Loops
338
+
339
+ Don't update dependencies inside the same effect:
340
+
341
+ ```typescript
342
+ const $count = state(0)
343
+
344
+ // ❌ Infinite loop!
345
+ effect((t) => {
346
+ const count = $count.get(t)
347
+ $count.set(count + 1) // Triggers itself!
348
+ })
349
+
350
+ // ✅ Use a different state
351
+ effect((t) => {
352
+ const count = $count.get(t)
353
+ $doubled.set(count * 2) // Updates different state
354
+ })
355
+ ```
356
+
357
+ ### Use Signals for Event-Only Tracking
358
+
359
+ When you don't need a value, just an event:
360
+
361
+ ```typescript
362
+ const $refresh = signal()
363
+
364
+ effect((t) => {
365
+ $refresh.watch(t) // Track signal without reading a value
366
+ fetchData()
367
+ })
368
+
369
+ // Trigger refresh
370
+ $refresh.trigger()
371
+ ```
372
+
373
+ ## Common Pitfalls
374
+
375
+ ### Forgetting to Use `.get(t)`
376
+
377
+ **Problem**: Using `.pick()` instead of `.get(t)` breaks reactivity.
378
+
379
+ ```typescript
380
+ // ❌ No dependency created - effect runs only once
381
+ effect((t) => {
382
+ console.log($count.pick()) // Using .pick() instead of .get(t)
383
+ })
384
+
385
+ $count.set(10) // Effect won't run
386
+ ```
387
+
388
+ **Solution**: Always use `.get(t)` to create dependencies:
389
+
390
+ ```typescript
391
+ // ✅ Correct
392
+ effect((t) => {
393
+ console.log($count.get(t))
394
+ })
395
+
396
+ $count.set(10) // Effect runs
397
+ ```
398
+
399
+ ### Not Disposing Effects
400
+
401
+ **Problem**: Effects continue running even when no longer needed.
402
+
403
+ ```typescript
404
+ // ❌ Memory leak in component lifecycle
405
+ function MyComponent() {
406
+ effect((t) => {
407
+ updateView($data.get(t))
408
+ })
409
+ // Never disposed!
410
+ }
411
+
412
+ // Each component creation adds another effect that never stops
413
+ ```
414
+
415
+ **Solution**: Always dispose effects when done:
416
+
417
+ ```typescript
418
+ // ✅ Proper cleanup
419
+ function MyComponent() {
420
+ const fx = effect((t) => {
421
+ updateView($data.get(t))
422
+ })
423
+
424
+ return {
425
+ cleanup: () => fx.dispose()
426
+ }
427
+ }
428
+ ```
429
+
430
+ ### Async Operations Without Care
431
+
432
+ **Problem**: Race conditions when using async operations in effects.
433
+
434
+ ```typescript
435
+ // ⚠️ Race condition possible
436
+ effect(async (t) => {
437
+ const id = $userId.get(t)
438
+ const data = await fetchUser(id) // If $userId changes during fetch...
439
+ displayUser(data) // ...we might display wrong user!
440
+ })
441
+ ```
442
+
443
+ **Solution**: Check if values are still relevant after async operations:
444
+
445
+ ```typescript
446
+ // ✅ Better - check if still relevant
447
+ effect(async (t) => {
448
+ const id = $userId.get(t)
449
+ const data = await fetchUser(id)
450
+
451
+ // Verify ID didn't change during fetch
452
+ if ($userId.pick() === id) {
453
+ displayUser(data)
454
+ }
455
+ })
456
+ ```
457
+
458
+ **Why this matters**: Between starting an async operation and its completion, your reactive values might change. Always verify the context is still valid before applying results.