|
| 1 | +--- |
| 2 | +title: Reactive $: statements |
| 3 | +--- |
| 4 | + |
| 5 | +In runes mode, reactions to state updates are handled with the [`$derived`]($derived) and [`$effect`]($effect) runes. |
| 6 | + |
| 7 | +In legacy mode, any top-level statement (i.e. not inside a block or a function) can be made reactive by prefixing it with a `$:` [label](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label). These statements run after other code in the `<script>` and before the component markup is rendered, then whenever the values that they depend on change. |
| 8 | + |
| 9 | +```svelte |
| 10 | +<script> |
| 11 | + let a = 1; |
| 12 | + let b = 2; |
| 13 | +
|
| 14 | + // this is a 'reactive statement', and it will re-run |
| 15 | + // when `a`, `b` or `sum` change |
| 16 | + $: console.log(`${a} + ${b} = ${sum}`); |
| 17 | +
|
| 18 | + // this is a 'reactive assignment' — `sum` will be |
| 19 | + // recalculated when `a` or `b` change. It is |
| 20 | + // not necessary to declare `sum` separately |
| 21 | + $: sum = a + b; |
| 22 | +</script> |
| 23 | +``` |
| 24 | + |
| 25 | +Statements are ordered _topologically_ by their dependencies and their assignments: since the `console.log` statement depends on `sum`, `sum` is calculated first even though it appears later in the source. |
| 26 | + |
| 27 | +Multiple statements can be combined by putting them in a block: |
| 28 | + |
| 29 | +```js |
| 30 | +// @noErrors |
| 31 | +$: { |
| 32 | + // recalculate `total` when `items` changes |
| 33 | + total = 0; |
| 34 | + |
| 35 | + for (const item of items) { |
| 36 | + total += item.value; |
| 37 | + } |
| 38 | +} |
| 39 | +``` |
| 40 | + |
| 41 | +The left-hand side of a reactive assignments can be an identifier, or it can be a destructuring assignment: |
| 42 | + |
| 43 | +```js |
| 44 | +// @noErrors |
| 45 | +$: ({ larry, moe, curly } = stooges); |
| 46 | +``` |
| 47 | + |
| 48 | +## Understanding dependencies |
| 49 | + |
| 50 | +The dependencies of a `$:` statement are determined at compile time — they are whichever variables are referenced (but not assigned to) inside the statement. |
| 51 | + |
| 52 | +In other words, a statement like this will _not_ re-run when `count` changes, because the compiler cannot 'see' the dependency: |
| 53 | + |
| 54 | +```js |
| 55 | +// @noErrors |
| 56 | +let count = 0; |
| 57 | +let double = () => count * 2; |
| 58 | + |
| 59 | +$: doubled = double(); |
| 60 | +``` |
| 61 | + |
| 62 | +Similarly, topological ordering will fail if dependencies are referenced indirectly: `z` will never update, because `y` is not considered 'dirty' when the update occurs. Moving `$: z = y` below `$: setY(x)` will fix it: |
| 63 | + |
| 64 | +```svelte |
| 65 | +<script> |
| 66 | + let x = 0; |
| 67 | + let y = 0; |
| 68 | +
|
| 69 | + $: z = y; |
| 70 | + $: setY(x); |
| 71 | +
|
| 72 | + function setY(value) { |
| 73 | + y = value; |
| 74 | + } |
| 75 | +</script> |
| 76 | +``` |
| 77 | + |
| 78 | +## Browser-only code |
| 79 | + |
| 80 | +Reactive statements run during server-side rendering as well as in the browser. This means that any code that should only run in the browser must be wrapped in an `if` block: |
| 81 | + |
| 82 | +```js |
| 83 | +// @noErrors |
| 84 | +$: if (browser) { |
| 85 | + document.title = title; |
| 86 | +} |
| 87 | +``` |
0 commit comments