Множественные контексты

Основы контекста

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

Создание контекста выполняется с помощью функции createContext:

import { createContext } from 'preact';
import { useContext } from 'preact/hooks';

const ThemeContext = createContext('light');

ThemeContext здесь создаёт объект контекста с дефолтным значением 'light'. Любой компонент в дереве может получить это значение через useContext(ThemeContext).

Провайдеры и множественные контексты

Для передачи значений используется компонент-провайдер Provider, который создаёт область видимости контекста:

<ThemeContext.Provider value="dark">
  <App />
</ThemeContext.Provider>

Когда приложение использует несколько контекстов одновременно, провайдеры можно вкладывать друг в друга:

import { createContext } from 'preact';
import { useContext } from 'preact/hooks';

const ThemeContext = createContext('light');
const AuthContext = createContext({ user: null });

function App() {
  const theme = useContext(ThemeContext);
  const auth = useContext(AuthContext);

  return (
    <div className={theme}>
      {auth.user ? `Привет, ${auth.user.name}` : 'Гость'}
    </div>
  );
}

function Root() {
  return (
    <ThemeContext.Provider value="dark">
      <AuthContext.Provider value={{ user: { name: 'Иван' } }}>
        <App />
      </AuthContext.Provider>
    </ThemeContext.Provider>
  );
}

Ключевой момент: порядок вложенности провайдеров имеет значение только для вложенных компонентов, но каждый контекст остаётся независимым. Компонент может использовать несколько контекстов одновременно, не создавая конфликтов.

Динамическое изменение контекста

Значения контекста можно изменять динамически. Это особенно полезно для темы оформления, состояния авторизации или языковых настроек:

import { useState } from 'preact/hooks';

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

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

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

  return (
    <button onCl ick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Сменить тему
    </button>
  );
}

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

Комбинирование контекстов через кастомные хуки

Для удобства работы с несколькими контекстами часто создаются кастомные хуки, которые агрегируют данные из нескольких источников:

function useAppContext() {
  const theme = useContext(ThemeContext);
  const auth = useContext(AuthContext);
  return { theme, auth };
}

function Dashboard() {
  const { theme, auth } = useAppContext();

  return (
    <div className={theme}>
      {auth.user ? `Добро пожаловать, ${auth.user.name}` : 'Пожалуйста, войдите'}
    </div>
  );
}

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

Влияние на производительность

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

  • Разделять контексты по независимым частям состояния.
  • Оборачивать провайдеры с различными областями ответственности отдельно.
  • Использовать мемоизацию значений через useMemo:
import { useMemo } from 'preact/hooks';

function AuthProvider({ children }) {
  const [user, setUser] = useState(null);

  const value = useMemo(() => ({ user, setUser }), [user]);

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

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

  • Избегать глубокой вложенности провайдеров, если это можно заменить локальным состоянием или кастомными хуками.
  • Группировать контексты по смыслу: например, все темы и настройки интерфейса в один, а авторизацию и данные пользователя — в другой.
  • Сохранять контекст как чистый объект без побочных эффектов внутри провайдера.

Множественные контексты в Preact позволяют гибко управлять состоянием и обеспечивают масштабируемую архитектуру приложения, сохраняя чистоту кода и минимизируя передачу пропсов вручную.