Derived stores

В SvelteKit derived stores представляют собой реактивные производные состояния, которые автоматически вычисляются на основе одного или нескольких других stores. Они позволяют создавать логически связанные данные, не дублируя состояние, и обеспечивают реактивность, характерную для Svelte.

import { writable, derived } from 'svelte/store';

const count = writable(0);
const doubled = derived(count, $count => $count * 2);

В этом примере doubled автоматически обновляется при изменении значения count. При обращении к $doubled получаем актуальное значение, без необходимости вручную подписываться на изменения count.


Синтаксис и структура

Одностороннее производное состояние

const storeA = writable(10);
const storeB = derived(storeA, $storeA => $storeA + 5);
  • storeA — исходный store, от которого зависит derived store.
  • $storeA — значение исходного store внутри функции.
  • Функция возвращает новое значение, которое будет храниться в storeB.

Множественные зависимости

Derived store может зависеть сразу от нескольких stores:

const firstName = writable('Иван');
const lastName = writable('Иванов');

const fullName = derived(
  [firstName, lastName],
  ([$firstName, $lastName]) => `${$firstName} ${$lastName}`
);
  • Первый аргумент — массив зависимостей.
  • Второй аргумент — функция, принимающая массив значений зависимых stores.
  • Возвращаемое значение автоматически обновляется при изменении любой зависимости.

Асинхронные вычисления

Derived store поддерживает асинхронные операции. Например, можно выполнять запросы к API и хранить результат как derived store:

const userId = writable(1);

const userData = derived(userId, ($userId, set) => {
  set(null); // начальное значение

  fetch(`https://jsonplaceholder.typicode.com/users/${$userId}`)
    .then(res => res.json())
    .then(data => set(data));
});
  • Второй аргумент функции может принимать set для обновления значения асинхронно.
  • Можно возвращать функцию очистки, которая будет вызвана при отписке или смене зависимости:
const timer = derived(count, ($count, set) => {
  const interval = setInterval(() => set($count + 1), 1000);
  return () => clearInterval(interval);
});

Ленивая и немедленная инициализация

Derived store вычисляет значение только при первой подписке. До подписки вычисления не выполняются, что экономит ресурсы:

const expensiveCalculation = derived(storeA, $storeA => {
  console.log('Вычисление запущено');
  return $storeA * 1000;
});

Сообщение Вычисление запущено появится только при первом обращении через $expensiveCalculation или подписке expensiveCalculation.subscribe(...).


Типичные сценарии использования

  1. Формирование вычисляемых строк или чисел:
const items = writable([1, 2, 3]);
const itemCount = derived(items, $items => $items.length);
  1. Комбинирование нескольких stores:
const a = writable(2);
const b = writable(3);
const sum = derived([a, b], ([$a, $b]) => $a + $b);
  1. Отслеживание состояния UI:
const isModalOpen = writable(false);
const modalClass = derived(isModalOpen, $isModalOpen =>
  $isModalOpen ? 'modal-open' : 'modal-closed'
);
  1. Асинхронная загрузка данных:
const query = writable('svelte');

const searchResults = derived(query, ($query, set) => {
  set([]);
  const controller = new AbortController();

  fetch(`https://api.example.com/search?q=${$query}`, { signal: controller.signal })
    .then(res => res.json())
    .then(data => set(data))
    .catch(err => {
      if (err.name !== 'AbortError') console.error(err);
    });

  return () => controller.abort();
});

Подписка и реактивность

Derived store можно использовать так же, как обычный writable store:

fullName.subscribe(value => console.log(value));

При этом каждый раз, когда изменяется хотя бы один исходный store, подписчики derived store получают новое значение.

Важно: derived store является чистым вычислением — он не хранит состояния самостоятельно, а всегда формирует его из зависимостей. Это обеспечивает согласованность данных и упрощает архитектуру приложения.


Оптимизация производительности

  • Использовать массив зависимостей вместо нескольких отдельных derived для связанных данных.
  • Минимизировать тяжелые вычисления внутри функции derived, либо кешировать результаты.
  • Для асинхронных операций обязательно предусматривать очистку (abort или clearInterval) при смене зависимостей.

Итоговая структура использования

  1. Определение исходных stores (writable, readable).
  2. Создание derived store через функцию derived.
  3. Подписка через $store или store.subscribe.
  4. Использование в компонентах SvelteKit как реактивного значения.

Derived stores позволяют строить сложные реактивные цепочки без лишнего кода и дублирования состояния, обеспечивая чистую архитектуру и гибкость при работе с UI в SvelteKit.