Провайдер контекста

Haunted — это лёгкая библиотека для создания веб-компонентов с использованием реактивного состояния и хуков, вдохновлённых React. Одной из ключевых возможностей для построения масштабируемых приложений является контекст, который позволяет компонентам обмениваться данными без необходимости пробрасывать их через каждый уровень иерархии.

Основные понятия контекста

Контекст в Haunted работает аналогично контексту в React. Он позволяет:

  • Создавать глобальные или локальные состояния, доступные нескольким компонентам.
  • Избежать “prop drilling”, когда свойства приходится передавать через множество уровней компонентов.
  • Разделять логику и данные между компонентами, сохраняя их реактивность.

Контекст состоит из двух частей:

  1. Provider — компонент или функция, которая предоставляет значение контекста.
  2. Consumer / useContext — механизм для потребления значения контекста внутри компонентов.

Создание провайдера контекста

Для создания контекста используется функция createContext. Она возвращает объект с полями Provider и useContext.

import { createContext, html, component, useState } from 'haunted';

const ThemeContext = createContext();

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => setTheme(theme === 'light' ? 'dark' : 'light');

  return html`
    <${ThemeContext.Provider} .value=${{ theme, toggleTheme }}>
      ${children}
    <//>
  `;
}

customElements.define('theme-provider', component(ThemeProvider));

Пояснения:

  • createContext() создаёт контекст, который можно использовать в любом компоненте.
  • ThemeContext.Provider оборачивает дочерние компоненты и предоставляет им объект состояния value.
  • Значение value может быть любым: объектом, строкой, функцией.

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

Компоненты, находящиеся внутри провайдера, могут получить доступ к контексту с помощью хука useContext.

import { useContext } from 'haunted';

function ThemeSwitcher() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return html`
    <button @click=${toggleTheme}>
      Сменить тему (текущая: ${theme})
    </button>
  `;
}

customElements.define('theme-switcher', component(ThemeSwitcher));

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

  • useContext автоматически подписывает компонент на изменения контекста. Если value изменится, компонент перерисуется.
  • Контекст можно использовать только внутри компонентов, обернутых соответствующим Provider.

Вложенные провайдеры

Haunted позволяет использовать вложенные провайдеры для создания локальных контекстов с переопределёнными значениями. При этом внутренний провайдер перекрывает значение внешнего для дочерних компонентов.

function App() {
  return html`
    <${ThemeProvider}>
      <theme-switcher></theme-switcher>
      <${ThemeContext.Provider} .value=${{ theme: 'custom', toggleTheme: () => {} }}>
        <theme-switcher></theme-switcher>
      <//>
    <//>
  `;
}

В этом примере первый theme-switcher использует значение 'light' или 'dark', а второй — 'custom'.

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

Для сложных приложений часто требуется, чтобы контекст содержал реактивное состояние, которое изменяется динамически. Haunted позволяет использовать любые хуки внутри провайдера, включая useState и useReducer.

function CounterProvider({ children }) {
  const [count, setCount] = useState(0);

  const increment = () => setCount(count + 1);

  return html`
    <${CounterContext.Provider} .value=${{ count, increment }}>
      ${children}
    <//>
  `;
}

function CounterDisplay() {
  const { count, increment } = useContext(CounterContext);

  return html`
    <div>
      Счётчик: ${count}
      <button @click=${increment}>+</button>
    </div>
  `;
}

Ключевой момент: при любом обновлении count все компоненты, использующие этот контекст, автоматически перерисуются.

Советы по использованию провайдеров

  • Располагать провайдер как можно выше в иерархии компонентов, если данные нужны многим дочерним компонентам.
  • Использовать отдельные контексты для разных типов данных (например, тема, авторизация, локализация), чтобы избежать ненужных перерисовок.
  • Переопределение значений через вложенные провайдеры удобно для локальных изменений без влияния на глобальный контекст.

Примеры практических сценариев

  1. Тема и стилизация компонентов: провайдер хранит текущую тему (light/dark) и функции переключения, а дочерние компоненты используют useContext для применения стилей.
  2. Авторизация пользователя: провайдер содержит информацию о пользователе и метод выхода, что позволяет любому компоненту проверять права доступа.
  3. Глобальный state management: контекст с useReducer может заменить простую версию Redux, предоставляя централизованное состояние для приложения.

Провайдер контекста в Haunted становится центральным инструментом для управления состоянием и логикой на уровне компонентов, обеспечивая чистоту кода и реактивность без избыточной передачи свойств.