Context API: обзор

Context API — встроенный механизм React для передачи данных через дерево компонентов без явной передачи пропсов на каждом уровне (без props drilling).

Зачем нужно

Когда несколько компонентов на разных уровнях дерева нуждаются в одних данных (текущий пользователь, тема, язык), передавать их через props на каждом уровне громоздко. Context создаёт «глобальное» хранилище в рамках поддерева, которое любой потребитель читает напрямую. Это встроенное решение — без сторонних библиотек — подходит для средне-сложного состояния; для сложного state management рассматривают Redux или Zustand.

Где используется

  • Тема приложения (светлая/тёмная): ThemeContext
  • Авторизация: AuthContext с текущим пользователем
  • Локализация: выбранный язык и функция перевода
  • Корзина покупок в небольшом e-commerce SPA

Как работает Context API

import { createContext, useContext, useState } from 'react';

// 1. Создание контекста с дефолтным значением
const ThemeContext = createContext('light');

// 2. Provider — оборачивает поддерево и передаёт значение
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  };

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

// 3. Consumer через useContext — читает текущее значение
function ThemedButton({ children }) {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <button
      style={{
        background: theme === 'light' ? '#fff' : '#333',
        color: theme === 'light' ? '#333' : '#fff',
      }}
      onClick={toggleTheme}
    >
      {children}
    </button>
  );
}

// 4. Использование в приложении
function App() {
  return (
    <ThemeProvider>
      <header>
        <ThemedButton>Переключить тему</ThemedButton>
      </header>
      <main>
        {/* Любой вложенный компонент может использовать useContext(ThemeContext) */}
      </main>
    </ThemeProvider>
  );
}

Паттерн: кастомный хук для контекста

// Лучшая практика — скрыть useContext за кастомным хуком
function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme должен использоваться внутри ThemeProvider');
  }
  return context;
}

// Теперь компоненты используют:
const { theme } = useTheme;
// вместо:
const { theme } = useContext(ThemeContext);

Context vs Props vs Redux

Пропсы        → для 1-2 уровней, локальные данные
Context API   → для 3+ уровней, данные всего поддерева (тема, auth)
Redux/Zustand → сложный state с действиями, middleware, DevTools

Частые ошибки

  • Лишние ре-рендеры — каждый потребитель useContext перерендеривается при изменении любой части значения контекста; разделяйте контексты по смыслу (отдельно для данных, отдельно для экшенов).
  • Мутация значения контекста напрямую — нужно всегда вызывать setState-функцию, а не изменять объект.
  • Используют Context для часто меняющихся данных — например, позиция курсора или анимации вызовут лавину ре-рендеров; для таких случаев лучше refs или специализированные библиотеки.

Связанные темы

Ресурсы