Контекст и провайдеры

SkateJS — библиотека для создания веб-компонентов на базе стандартов Custom Elements, предоставляющая высокоуровневые абстракции для работы с состоянием, рендерингом и реактивностью. Одним из ключевых механизмов, обеспечивающих гибкую архитектуру компонентов, являются контекст и провайдеры. Эти инструменты позволяют организовать обмен данными между компонентами без необходимости передачи их через цепочку пропсов.


Контекст: определение и назначение

Контекст — это механизм, с помощью которого компонент может предоставить данные всем своим потомкам без явной передачи через свойства (props). В SkateJS контексты реализуются через функцию createContext, которая возвращает объект с двумя основными элементами:

  • Provider — компонент, предоставляющий контекст.
  • Consumer — компонент, получающий данные контекста.

Использование контекста позволяет решать задачи глобального состояния внутри локального дерева компонентов, например:

  • управление темой интерфейса;
  • хранение информации о текущем пользователе;
  • централизованное управление состоянием форм.

Создание контекста

Контекст создается вызовом createContext с указанием начального значения:

import { createContext } from 'skatejs';

const ThemeContext = createContext('light');

В данном примере создается контекст ThemeContext с начальным значением 'light'. Это значение будет использоваться всеми компонентами-потомками, если не будет переопределено провайдером.


Провайдеры

Провайдер — это компонент, который задает значение контекста для всех вложенных компонентов. В SkateJS провайдер создается автоматически через Provider объекта контекста. Для передачи нового значения используется свойство value.

import { h, define } from 'skatejs';

define('theme-provider', {
  render: ({ host }) => (
    <ThemeContext.Provider value="dark">
      <slot></slot>
    </ThemeContext.Provider>
  )
});

Все компоненты внутри <theme-provider> получат значение контекста 'dark'.

Особенности работы провайдера:

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

Потребление контекста

Компоненты-потребители используют Consumer или хук useContext для доступа к текущему значению:

define('theme-display', {
  render: () => (
    <ThemeContext.Consumer>
      {theme => <div>Текущая тема: {theme}</div>}
    </ThemeContext.Consumer>
  )
});

Результат рендеринга зависит от ближайшего родительского провайдера. Если провайдер отсутствует, используется значение по умолчанию, заданное при создании контекста.


Хуки для контекста

SkateJS поддерживает реактивные хуки, упрощающие работу с контекстом внутри функциональных компонентов:

import { useContext } from 'skatejs';

define('theme-display-hook', {
  render: () => {
    const theme = useContext(ThemeContext);
    return <div>Текущая тема через хук: {theme}</div>;
  }
});

Преимущества использования хуков:

  • Упрощение синтаксиса по сравнению с Consumer.
  • Легкая интеграция с реактивными состояниями.
  • Автоматическая подписка на изменения контекста.

Вложенные провайдеры и переопределение контекста

Контексты могут быть переопределены несколькими уровнями вложенности:

<ThemeContext.Provider value="light">
  <ThemeContext.Provider value="dark">
    <theme-display></theme-display>
  </ThemeContext.Provider>
</ThemeContext.Provider>

Компонент <theme-display> внутри второго провайдера получит значение 'dark', игнорируя внешний провайдер. Такой подход позволяет строить локальные зоны конфигурации внутри приложения без влияния на глобальные настройки.


Практические рекомендации

  • Использовать контексты для сквозных данных, которые нужны множеству компонентов.
  • Не злоупотреблять контекстами для редко используемых данных, так как это увеличивает связанность.
  • Для динамически изменяемых значений применять useContext с реактивным состоянием, чтобы обновления автоматически рендерились.
  • Комбинировать контексты для создания сложных структур, например, темы + локализация + аутентификация.

Примеры реального применения

  1. Темизация компонентов: единый провайдер ThemeContext управляет светлой и темной темами интерфейса.
  2. Глобальные настройки форм: провайдер FormContext передает конфигурацию валидации для всех полей формы.
  3. Локализация: LocaleContext определяет язык и региональные настройки, доступные всему дереву компонентов.

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