Пользовательские stores и их реализация

В Svelte stores — это способ управления состоянием, который позволяет создавать реактивные данные, доступные по всему приложению. Пользовательские stores представляют собой особый вид store, который можно настроить и расширить под специфические нужды. В отличие от стандартных встроенных stores, таких как writable, readable и derived, пользовательские stores дают возможность внедрять кастомную логику, а также управлять поведением хранения данных более гибко.

Что такое store?

Store в Svelte — это объект, который хранит данные и уведомляет подписчиков о любых изменениях этих данных. Каждый store в Svelte является подписным объектом. То есть компоненты, подписанные на store, будут автоматически обновляться, когда store изменяет своё состояние.

В Svelte существуют несколько типов встроенных stores:

  • writable: store, с которым можно как читать, так и изменять данные.
  • readable: store, с которым только можно читать данные.
  • derived: store, который зависит от другого store.

Пользовательские stores, как правило, реализуются с использованием базовых типов, но предоставляют дополнительные возможности для обработки данных.

Реализация пользовательского store

Для создания собственного store необходимо реализовать объект, который подписывает подписчиков на изменения состояния и предоставляет методы для управления этим состоянием. Основная задача пользовательского store — обеспечить нужную логику для работы с состоянием, будь то синхронные или асинхронные операции.

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

function createCounter() {
  const { subscribe, set, UPDATE } = writable(0);

  return {
    subscribe,
    increment: () => UPDATE(n => n + 1),
    decrement: () => UPDATE(n => n - 1),
    reset: () => se t(0)
  };
}

export const counter = createCounter();

В этом примере создается простой counter store, который использует базовый writable store. Метод increment увеличивает счетчик, метод decrement уменьшает его, а метод reset сбрасывает значение в ноль. Вся логика изменяется с помощью методов update и set.

Основные принципы реализации

  1. Использование writable: Все пользовательские stores, как правило, начинают с использования writable store, так как он предоставляет все необходимые методы для работы с данными: set, update и subscribe.

  2. Метод subscribe: Это базовая функция, которая подписывает компонент на изменения данных. Возвращаемый из store объект subscribe позволяет любому компоненту подписаться на store и автоматически обновляться при его изменении.

  3. Методы управления состоянием: Внутри пользовательского store можно создать любые методы для управления состоянием. Важно помнить, что любые изменения состояния должны осуществляться через методы set или update, так как они обеспечивают реактивность.

  4. Асинхронные операции: Если нужно работать с асинхронными данными (например, с запросами к серверу), пользовательские stores могут быть расширены для работы с промисами или другими асинхронными механизмами.

Пример асинхронного store:

import { writable } from 'svelte/store';

function createAsyncData() {
  const { subscribe, se t, UPDATE } = writable({ loading: false, data: null, error: null });

  return {
    subscribe,
    fetchData: async (url) => {
      UPDATE(state => ({ ...state, loading: true }));
      try {
        const response = await fetch(url);
        const data = await response.json();
        se t({ loading: false, data, error: null });
      } catch (err) {
        se t({ loading: false, data: null, error: err.message });
      }
    }
  };
}

export const asyncData = createAsyncData();

Этот store загружает данные с сервера, обновляя состояние с помощью метода set и обрабатывает ошибки с использованием асинхронной логики.

Роль set, update, и subscribe

  • set: Метод, который напрямую устанавливает новое значение в store и уведомляет всех подписчиков о его изменении.

  • update: Метод, который позволяет модифицировать текущее состояние store. Он получает функцию, которая принимает старое состояние и возвращает новое.

  • subscribe: Функция, которая позволяет компонентам подписываться на store и получать уведомления о его изменениях. При изменении состояния store все подписчики обновляются автоматически.

Работа с несколькими stores

Иногда необходимо использовать несколько stores в одном компоненте. Для этого достаточно подписаться на несколько store или использовать их комбинированное значение.

Пример комбинирования нескольких stores с помощью derived:

import { derived } from 'svelte/store';
import { counter } from './counterStore';
import { asyncData } from './asyncDataStore';

export const combinedState = derived(
  [counter, asyncData],
  ([$counter, $asyncData]) => ({
    counter: $counter,
    asyncData: $asyncData.data
  })
);

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

Инкапсуляция и абстракция

Пользовательские stores могут инкапсулировать сложную логику работы с состоянием и абстрагировать её от компонентов. Это позволяет легко управлять состоянием и делать компоненты менее зависимыми от внутренней логики хранения данных.

Например, можно создавать stores, которые управляют состоянием формы:

import { writable } from 'svelte/store';

function createForm() {
  const { subscribe, set, UPDATE } = writable({
    name: '',
    email: ''
  });

  return {
    subscribe,
    updateField: (field, value) => update(state => ({ ...state, [field]: value })),
    reset: () => se t({ name: '', email: '' })
  };
}

export const formStore = createForm();

Этот store управляет состоянием формы, позволяя обновлять отдельные поля или сбрасывать все значения.

Подписка на store внутри компонента

Для использования пользовательского store в Svelte необходимо подписаться на него внутри компонента. Для этого в компонентах используется директива $, которая позволяет напрямую привязать данные из store к компоненту.

Пример:

<script>
  import { counter } from './counterStore';
</script>

<button on:click={() => $counter.increment()}>Increment</button>
<p>Counter: {$counter}</p>

Здесь $counter автоматически подписывает компонент на изменения store, а реактивное обновление происходит при каждом изменении значения.

Заключение

Пользовательские stores в Svelte — мощный инструмент для управления состоянием с возможностью добавления пользовательской логики. Они позволяют инкапсулировать и абстрагировать логику работы с данными, создавая гибкую архитектуру приложений. Использование кастомных stores помогает поддерживать чистоту кода, улучшает его модульность и позволяет эффективно управлять состоянием в крупных приложениях.