2.3 KiB
title |
---|
$derived |
Derived state is declared with the $derived
rune:
<script>
let count = $state(0);
let doubled = $derived(count * 2);
</script>
<button onclick={() => count++}>
{doubled}
</button>
<p>{count} doubled is {doubled}</p>
The expression inside $derived(...)
should be free of side-effects. Svelte will disallow state changes (e.g. count++
) inside derived expressions.
As with $state
, you can mark class fields as $derived
.
[!NOTE] Code in Svelte components is only executed once at creation. Without the
$derived
rune,doubled
would maintain its original value even whencount
changes.
$derived.by
Sometimes you need to create complex derivations that don't fit inside a short expression. In these cases, you can use $derived.by
which accepts a function as its argument.
<script>
let numbers = $state([1, 2, 3]);
let total = $derived.by(() => {
let total = 0;
for (const n of numbers) {
total += n;
}
return total;
});
</script>
<button onclick={() => numbers.push(numbers.length + 1)}>
{numbers.join(' + ')} = {total}
</button>
In essence, $derived(expression)
is equivalent to $derived.by(() => expression)
.
Understanding dependencies
Anything read synchronously inside the $derived
expression (or $derived.by
function body) is considered a dependency of the derived state. When the state changes, the derived will be marked as dirty and recalculated when it is next read.
To exempt a piece of state from being treated as a dependency, use untrack
.
Update propagation
Svelte uses something called push-pull reactivity — when state is updated, everything that depends on the state (whether directly or indirectly) is immediately notified of the change (the 'push'), but derived values are not re-evaluated until they are actually read (the 'pull').
If the new value of a derived is referentially identical to its previous value, downstream updates will be skipped. In other words, Svelte will only update the text inside the button when large
changes, not when count
changes, even though large
depends on count
:
<script>
let count = $state(0);
let large = $derived(count > 10);
</script>
<button onclick={() => count++}>
{large}
</button>