Перейти к содержимому
GitHub

$derived

Производное состояние объявляется с помощью руны $derived:

<script>
  let count = $state(0);
  let doubled = $derived(count * 2);
</script>

<button onclick={() => count++}>
  {doubled}
</button>

<p>{count} вдвое больше — это {doubled}</p>

Выражение внутри $derived(...) должно быть свободно от побочных эффектов. Svelte не позволит изменения состояния (например, count++) внутри производных выражений.

Как и при использовании $state, вы можете пометить поля класса как $derived.

$derived.by

Иногда вам нужно создать сложные производные, которые не помещаются в короткое выражение. В таких случаях вы можете использовать $derived.by, который принимает функцию в качестве аргумента.

<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>

По сути, $derived(expression) эквивалентно $derived.by(() => expression).

Понимание зависимостей

Всё, что читается синхронно внутри выражения $derived (или тела функции $derived.by), считается зависимостью производного состояния. Когда состояние изменяется, производное будет помечено как грязное и пересчитано при следующем чтении.

Чтобы исключить часть состояния из обработки как зависимость, используйте untrack.

Переопределение производных значений

Производные выражения пересчитываются при изменении их зависимостей, но вы можете временно переопределить их значения, присвоив им новое значение (если они не объявлены с помощью const). Это может быть полезно для таких вещей, как оптимистичный UI, где значение вычисляется на основе «источника истины» (например, данных с сервера), но вы хотите сразу показать пользователю обратную связь:

<script>
  let { post, like } = $props();

  let likes = $derived(post.likes);

  async function onclick() {
    // Немедленно увеличиваем счетчик `likes`...
    likes += 1;

    // и уведомляем сервер, который в конечном итоге обновит `post`
    try {
      await like();
    } catch {
      // Ошибка! Откатываем изменение
      likes -= 1;
    }
  }
</script>

<button {onclick}>🧡 {likes}</button>

Производные значения и реактивность

В отличие от $state, который преобразует объекты и массивы в глубоко реактивные прокси, значения $derived остаются как есть. Например, в таком случае

let items = $state([...]);

let index = $state(0);
let selected = $derived(items[index]);

…вы можете изменять (или использовать bind: для) свойства selected, и это повлияет на базовый массив items. Если бы items не был глубоко реактивным, изменение selected не имело бы эффекта.

Обновление данных

Svelte использует механизм, известный как реактивность с пуш-пулом — когда состояние изменяется, все, что связано с этим состоянием (прямо или косвенно), немедленно получает уведомление об изменении (пуш), но производные значения не пересчитываются, пока они не будут фактически запрошены (пул).

Если новое значение производного совпадает с предыдущим по ссылке, обновления для зависимых компонентов будут пропущены. То есть, Svelte обновит текст внутри кнопки только тогда, когда изменится large, а не count, даже несмотря на то, что large зависит от count:

<script>
  let count = $state(0);
  let large = $derived(count > 10);
</script>

<button onclick={() => count++}>
  {large}
</button>