You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- explain when not to use `$effect`, closes#10193
- explain that only synchronous reads are tracked, closes#10475
- explain nuance around reruns and object reads, closes#10392
@@ -161,24 +161,101 @@ In essence, `$derived(expression)` is equivalent to `$derived.by(() => expressio
161
161
162
162
To run side-effects like logging or analytics whenever some specific values change, or when a component is mounted to the DOM, we can use the `$effect` rune:
163
163
164
-
```diff
164
+
```svelte
165
+
<script>
166
+
let count = $state(0);
167
+
let doubled = $derived(count * 2);
168
+
169
+
$effect(() => {
170
+
console.log({ count, doubled });
171
+
});
172
+
</script>
173
+
174
+
<button on:click={() => count++}>
175
+
{doubled}
176
+
</button>
177
+
178
+
<p>{count} doubled is {doubled}</p>
179
+
```
180
+
181
+
`$effect` will automatically subscribe to any `$state` or `$derived` values it reads _synchronously_ and reruns whenever their values change — that means, values after an `await` or inside a `setTimeout` will _not_ be tracked. `$effect` will run after the DOM has been updated.
182
+
183
+
```svelte
184
+
<script>
185
+
let count = $state(0);
186
+
let doubled = $derived(count * 2);
187
+
188
+
$effect(() => {
189
+
// runs after the DOM has been updated
190
+
// when the component is mounted
191
+
// and whenever `count` changes,
192
+
// but not when `doubled` changes,
193
+
console.log(count);
194
+
195
+
setTimeout(() => console.log(doubled));
196
+
});
197
+
</script>
198
+
199
+
<button on:click={() => count++}>
200
+
{doubled}
201
+
</button>
202
+
203
+
<p>{count} doubled is {doubled}</p>
204
+
```
205
+
206
+
An effect only reruns when the object it reads changes, not when a property inside it changes. If you want to react to _any_ change inside an object for inspection purposes at dev time, you may want to use [`inspect`](#inspect).
207
+
208
+
```svelte
209
+
<script>
210
+
let object = $state({ count: 0 });
211
+
let derived_object = $derived({
212
+
doubled: object.count * 2
213
+
});
214
+
215
+
$effect(() => {
216
+
// never reruns, because object does not change,
217
+
// only its property changes
218
+
object;
219
+
console.log('object');
220
+
});
221
+
222
+
$effect(() => {
223
+
// reruns, because object.count changes
224
+
object.count;
225
+
console.log('object.count');
226
+
});
227
+
228
+
$effect(() => {
229
+
// reruns, because $derived produces a new object on each rerun
230
+
derived_object;
231
+
console.log('derived_object');
232
+
});
233
+
</script>
234
+
235
+
<button on:click={() => object.count++}>
236
+
{doubled}
237
+
</button>
238
+
239
+
<p>{count} doubled is {doubled}</p>
240
+
```
241
+
242
+
You can return a function from `$effect`, which will run immediately before the effect re-runs, and before it is destroyed.
243
+
244
+
```svelte
165
245
<script>
166
246
let count = $state(0);
167
247
let doubled = $derived(count * 2);
168
248
169
-
+ $effect(() => {
170
-
+ // runs when the component is mounted, and again
171
-
+ // whenever `count` or `doubled` change,
172
-
+ // after the DOM has been updated
173
-
+ console.log({ count, doubled });
174
-
+
175
-
+ return () => {
176
-
+ // if a callback is provided, it will run
177
-
+ // a) immediately before the effect re-runs
178
-
+ // b) when the component is destroyed
179
-
+ console.log('cleanup');
180
-
+ };
181
-
+ });
249
+
$effect(() => {
250
+
console.log({ count, doubled });
251
+
252
+
return () => {
253
+
// if a callback is provided, it will run
254
+
// a) immediately before the effect re-runs
255
+
// b) when the component is destroyed
256
+
console.log('cleanup');
257
+
};
258
+
});
182
259
</script>
183
260
184
261
<button on:click={() => count++}>
@@ -188,6 +265,93 @@ To run side-effects like logging or analytics whenever some specific values chan
188
265
<p>{count} doubled is {doubled}</p>
189
266
```
190
267
268
+
> `$effect` was designed for managing side effects such as logging or connecting to external systems like third party libraries that have an imperative API. If you're managing state or dataflow, you should use it with caution – most of the time, you're better off using a different pattern. Below are some use cases and what to use instead.
269
+
270
+
If you update `$state` inside an `$effect`, you most likely want to use `$derived` instead.
271
+
272
+
```svelte
273
+
<script>
274
+
let count = $state(0);
275
+
// Don't do this:
276
+
let doubled = $state();
277
+
$effect(() => {
278
+
doubled = count * 2;
279
+
});
280
+
// Do this instead:
281
+
let doubled = $derived(count * 2);
282
+
</script>
283
+
```
284
+
285
+
This also applies to more complex calculations that require more than a simple expression and write to more than one variable. In these cases, you can use `$derived.by`.
286
+
287
+
```svelte
288
+
<script>
289
+
// Don't do this:
290
+
let result_1 = $state();
291
+
let result_2 = $state();
292
+
$effect(() => {
293
+
// ... some lengthy code resulting in
294
+
result_1 = someValue;
295
+
result_2 = someOtherValue;
296
+
});
297
+
// Do this instead:
298
+
let { result_1, result_2 } = $derived.by(() => {
299
+
// ... some lengthy code resulting in
300
+
return {
301
+
result_1: someValue,
302
+
result_2: someOtherValue
303
+
};
304
+
});
305
+
</script>
306
+
```
307
+
308
+
When reacting to a state change and writing to a different state as a result, think about if it's possible to model the code through event handling instead.
309
+
310
+
```svelte
311
+
<!-- Don't do this -->
312
+
<script>
313
+
let value = $state();
314
+
let value_uppercase = $state();
315
+
$effect(() => {
316
+
value_uppercase = value.toUpperCase();
317
+
});
318
+
</script>
319
+
320
+
<Text bind:value />
321
+
322
+
<!-- Do this instead: -->
323
+
<script>
324
+
let value = $state();
325
+
let value_uppercase = $state();
326
+
function onValueChange(new_text) {
327
+
value = new_text;
328
+
value_uppercase = new_text.toUpperCase();
329
+
}
330
+
</script>
331
+
332
+
<Text {value} {onValueChange}>
333
+
```
334
+
335
+
If you want to have something update from above but also modify it from below (i.e. you want some kind of "writable `$derived`"), and events aren't an option, you can also use an object with getters and setters.
336
+
337
+
```svelte
338
+
<script>
339
+
let { value } = $props();
340
+
let proxy = {
341
+
get value() {
342
+
return value.toUpperCase();
343
+
},
344
+
set value(val) {
345
+
value = val.toLowerCase();
346
+
}
347
+
};
348
+
</script>
349
+
350
+
<input bind:value={proxy.value} />
351
+
```
352
+
353
+
If you absolutely have to update `$state` within an effect and run into an infinite loop because you read and write to the same `$state`, use [untrack](functions#untrack).
354
+
191
355
### What this replaces
192
356
193
357
The portions of `$: {}` that are triggering side-effects can be replaced with `$effect` while being careful to migrate updates of reactive variables to use `$derived`. There are some important differences:
@@ -236,6 +400,8 @@ In rare cases, you may need to run code _before_ the DOM updates. For this we ca
236
400
</div>
237
401
```
238
402
403
+
Apart from the timing, `$effect.pre` works exactly like [`$effect`](#effect) — refer to its documentation for more info.
404
+
239
405
### What this replaces
240
406
241
407
Previously, you would have used `beforeUpdate`, which — like `afterUpdate` — is deprecated in Svelte 5.
0 commit comments