Создание хранилища

Работа со SkateJS предполагает использование собственного хранилища состояния для управления данными приложения. Хранилище служит централизованным источником истины, упрощая синхронизацию интерфейса и логики. SkateJS использует реактивный подход, позволяя компонентам подписываться на изменения и перерабатывать представление только при необходимости.

Хранилище в SkateJS обычно создаётся как отдельный модуль. Его задача — хранить структуру данных, методы обновления и механизмы подписки. На практике используется функция createStore, принимающая объект начального состояния и набор редукторов.

import { createStore } from 'skatejs/store';

const initialState = {
  tasks: [],
  filter: 'all'
};

const reducers = {
  addTask(state, payload) {
    return {
      ...state,
      tasks: [...state.tasks, payload]
    };
  },
  toggleTask(state, id) {
    return {
      ...state,
      tasks: state.tasks.map(t =>
        t.id === id ? { ...t, completed: !t.completed } : t
      )
    };
  },
  setFilter(state, filter) {
    return {
      ...state,
      filter
    };
  }
};

export const store = createStore(initialState, reducers);

Главные элементы — initialState и reducers. Первое задаёт отправную точку данных, второе описывает изменения. Ни один редуктор не изменяет состояние напрямую, что сохраняет детерминизм и предсказуемость.

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

После создания хранилища SkateJS предоставляет механизм подписки на изменения. Компоненты подключаются к состоянию через store.subscribe, и при обновлении выбранных данных инициируется обновление представления.

store.subscribe((state) => {
  renderApp(state);
});

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

Обновление состояния

Обновление состояния выполняется через вызов редуктора. SkateJS предоставляет метод store.dispatch, принимающий имя редуктора и нагрузку. Такой подход гарантирует прозрачность мутаций.

store.dispatch('addTask', { id: 1, text: 'Написать пример', completed: false });
store.dispatch('setFilter', 'completed');

Каждая операция возвращает новое состояние, сохраняя неизменяемость. Это упрощает отладку и интеграцию инструментов трассировки. Неизменяемость также способствует корректной работе диффинг-алгоритма в компонентах.

Наблюдение за структурами данных

Сложные структуры, такие как массивы или вложенные объекты, не требуют дополнительных средств протоколирования. SkateJS рассчитывает diffs и определяет, какие компоненты должны быть переработаны. Это избавляет от ручной оптимизации подписок.

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

Интеграция с компонентами

Компоненты SkateJS используют декораторы и привязку состояния. При подключении к хранилищу компонент объявляет, какие данные ему требуются. Далее SkateJS автоматически обновляет экземпляр компонента при изменении соответствующей части состояния.

import { withStore } from 'skatejs/store';

class TaskList extends withStore(HTMLElement) {
  static get storeSelectors() {
    return {
      tasks: (state) => state.tasks
    };
  }

  render() {
    return this.tasks.map(t => `<li>${t.text}</li>`).join('');
  }
}

customElements.define('task-list', TaskList);

Заметно, что компонент ограничивается своими данными, не затрагивая остальное состояние. Это повышает модульность и предотвращает повторные переработки.

Расширение хранилища

Хранилище допускает расширение без модификации существующей логики. Добавление новых редукторов или сегментов состояния не требует изменения остальных модулей приложения. Расширение достигается путём обновления набора редукторов или композиции нескольких хранилищ.

Композиция особенно полезна при росте проекта. Каждое доменное хранилище может отвечать за небольшой участок данных, а верхний уровень объединяет результаты. Такая архитектура упрощает тестирование и изолирует побочные эффекты.

Работа с асинхронными операциями

Асинхронные операции, например запросы к серверу, оформляются через функции-диспетчеры, возвращающие Promise. После выполнения Promise вызываются обычные редукторы для фиксации результата. SkateJS не навязывает конкретный middleware, что оставляет свободу выбора инструментов.

async function fetchTasks() {
  const tasks = await api.loadTasks();
  store.dispatch('setTasks', tasks);
}

Асинхронный слой остаётся поверх хранилища, не усложняя его внутреннюю модель.

Оптимизации и тестирование

Так как редукторы детерминированы, хранилище легко покрывается тестами. Достаточно передать состояние и нагрузку и сравнить итоговый объект. Неизменяемость гарантирует отсутствие скрытых побочных эффектов.

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

Итоговая структура хранилища

Типичное хранилище SkateJS включает:

Начальное состояние. Базовый объект данных. Редукторы. Чистые функции обновления. Подписки. Механизм реакции интерфейса. Асинхронные диспетчеры. Логика взаимодействия с внешними источниками. Композицию. Возможность разбиения на домены.

Такое устройство позволяет создавать масштабируемые и предсказуемые приложения, сохраняя чистоту архитектуры и упрощая сопровождение.