Работа с состоянием через stores

В SvelteKit управление состоянием компонентов и приложений осуществляется через механизм stores. Stores позволяют централизованно хранить и отслеживать значения, автоматически обновляя все компоненты, которые на них подписаны. Это особенно важно для крупных приложений, где данные должны быть согласованными между множеством компонентов.

SvelteKit предоставляет несколько видов stores: writable, readable и derived. Каждый из них решает определённые задачи.


Writable stores

Writable store — это изменяемый store, позволяющий напрямую изменять его значение. Для создания используется функция writable из пакета svelte/store.

import { writable } from 'svelte/store';

export const count = writable(0);

Доступ к значению store осуществляется через метод subscribe или через синтаксис $store внутри Svelte-компонента:

<script>
  import { count } from './stores.js';

  function increment() {
    count.update(n => n + 1);
  }
</script>

<p>Текущее значение: {$count}</p>
<button on:click={increment}>Увеличить</button>

Методы writable store:

  • set(value) — устанавливает новое значение.
  • upd ate(fn) — обновляет текущее значение, применяя функцию.
count.se t(10);
count.update(n => n * 2);

$count внутри Svelte-компонента — это реактивная привязка, автоматически подписывающая компонент на изменения store.


Readable stores

Readable store используется для хранения данных, которые изменяются извне, например, значения из API или таймера. Создаётся с помощью функции readable:

import { readable } from 'svelte/store';

export const time = readable(new Date(), set => {
  const interval = setInterval(() => {
    set(new Date());
  }, 1000);

  return () => clearInterval(interval);
});

Особенности readable:

  • Значение store можно только подписывать, нельзя напрямую изменять.
  • Функция внутри readable принимает set, позволяя контролировать обновления.
  • Возвращаемая функция используется для очистки ресурсов при отписке (например, остановка таймера).

В компоненте подписка через $time обеспечивает автоматическое обновление отображения:

<p>Текущее время: {$time.toLocaleTimeString()}</p>

Derived stores

Derived store позволяет создавать store на основе других store. Это удобно для вычисления значений, которые зависят от нескольких источников данных.

import { derived } from 'svelte/store';
import { count } from './stores.js';

export const doubleCount = derived(count, $count => $count * 2);

Особенности derived:

  • Автоматически пересчитывается при изменении зависимых store.
  • Можно использовать несколько store в массиве:
import { count, anotherValue } from './stores.js';

export const combined = derived([count, anotherValue], ([$count, $anotherValue]) => {
  return $count + $anotherValue;
});
  • Подписка на derived store аналогична подписке на обычный writable:
<p>Двойное значение: {$doubleCount}</p>

Стратегии использования stores в приложении

  1. Локальные stores — используются только в пределах одного модуля или компонента. Подходят для небольших состояний, таких как открытие модального окна.
  2. Глобальные stores — экспортируются из отдельного файла и подключаются в несколько компонентов. Полезны для управления авторизацией, корзиной товаров, глобальными настройками.
  3. Комбинированные stores — объединение writable, readable и derived для создания сложной логики с реактивными зависимостями.

Подписка и автоматическое отписывание

В компонентах Svelte используется синтаксис $store для автоматической подписки. При этом SvelteKit сам управляет отпиской при уничтожении компонента, что предотвращает утечки памяти. Для ручной подписки можно использовать subscribe:

import { count } from './stores.js';

const unsubscribe = count.subscribe(value => {
  console.log('Текущее значение:', value);
});

// Для остановки подписки
unsubscribe();

Асинхронные операции с store

Для интеграции с API или асинхронными источниками состояния writable store можно использовать комбинацию set и async функций:

import { writable } from 'svelte/store';

export const user = writable(null);

export async function fetchUser() {
  const res = await fetch('/api/user');
  const data = await res.json();
  user.set(data);
}

Подписка на $user в компоненте автоматически обновляет интерфейс после получения данных.


Практические советы

  • Минимизировать количество глобальных store, чтобы не создавать сложную сеть зависимостей.
  • Использовать derived для вычисляемых данных, вместо хранения их напрямую в writable.
  • Очищать ресурсы в readable через возвращаемую функцию, чтобы не оставлять активные таймеры или WebSocket-подключения.
  • Комбинировать stores с реактивными переменными в компонентах для упрощения логики отображения и обработки событий.

Интеграция с SvelteKit маршрутизацией

Stores легко использовать с маршрутизатором SvelteKit. Например, хранение текущего состояния страницы или параметров запроса:

import { writable } from 'svelte/store';
import { page } from '$app/stores';

export const currentQuery = derived(page, $page => $page.url.searchParams.get('q'));

Это позволяет автоматически обновлять компоненты при изменении URL или параметров запроса без ручного контроля.


SvelteKit stores предоставляют мощный и компактный инструмент для реактивного управления состоянием, позволяя строить сложные интерфейсы с минимальным количеством кода и высокой предсказуемостью поведения.