Что такое Context API в React?

MiddleReact · Frontend·Обновлено 22 июня 2026
Коротко
Context API — это встроенный механизм React для передачи данных через дерево компонентов без явной передачи пропсов на каждом уровне (prop drilling). Он позволяет сделать данные «глобальными» для поддерева компонентов.

Context API в React

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

Когда использовать Context

Context подходит для данных, которые можно считать «глобальными» для дерева компонентов: текущий пользователь, тема оформления, язык интерфейса, настройки. Для сложного серверного стейта или частых локальных обновлений лучше подойдут специализированные решения (Zustand, Redux, React Query).

Как устроен Context API

Context состоит из трёх частей:

  1. React.createContext(defaultValue) — создаёт объект контекста с дефолтным значением. Дефолт используется только если компонент находится вне дерева провайдера.
  2. Context.Provider — компонент-обёртка, передающий значение всем потребителям ниже по дереву.
  3. useContext(Context) — хук, подписывающий компонент на изменения контекста.
// Создание контекста с типом
const ThemeContext = React.createContext<'light' | 'dark'>('light');

Пример использования

// Провайдер оборачивает дерево
function App() {
  const [theme, setTheme] = React.useState<'light' | 'dark'>('light');

  return (
    <ThemeContext.Provider value={theme}>
      <Header />
      <Main />
    </ThemeContext.Provider>
  );
}

// Потребитель читает значение через хук
function Header() {
  const theme = useContext(ThemeContext);
  return <header className={theme}>...</header>;
}

Оптимизация и ре-рендеры

Каждое изменение value в провайдере вызывает ре-рендер всех подписанных через useContext компонентов, даже если они используют только часть данных. Основные способы оптимизации:

  • Разделение контекстов — хранить несвязанные данные в разных контекстах.
  • Мемоизация value через useMemo, чтобы не создавать новый объект на каждый рендер родителя.
  • Паттерн «разделение стейта и диспатча» — отдельный контекст для данных и отдельный для функций обновления.
const CountContext = React.createContext<number>(0);
const CountDispatchContext = React.createContext<React.Dispatch<React.SetStateAction<number>>>(() => {});

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

  // setCount стабилен — подписчики диспатча не ре-рендерятся при изменении count
  return (
    <CountDispatchContext.Provider value={setCount}>
      <CountContext.Provider value={count}>
        {children}
      </CountContext.Provider>
    </CountDispatchContext.Provider>
  );
}

Context vs внешний стейт-менеджер

Context хорош для редко меняющихся глобальных данных. Если данные обновляются часто или логика сложная — стоит рассмотреть Zustand или Redux Toolkit, которые предоставляют селекторы и позволяют подписываться только на нужный срез стейта.

Что хочет услышать интервьюер

Понимание проблемы prop drilling и того, как Context её решает

Знание трёх составляющих: createContext, Provider, useContext

Осознание, что Context вызывает ре-рендер всех подписчиков при изменении value

Умение оптимизировать контекст: разделение контекстов, useMemo для value

Понимание границ применимости Context vs внешних стейт-менеджеров

Пример: Базовый Context с провайдером и хуком-обёрткой

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

interface AuthContextValue {
  user: string | null;
  login: (name: string) => void;
  logout: () => void;
}

// Дефолт — null, будет использоваться только вне провайдера
const AuthContext = createContext<AuthContextValue | null>(null);

// Хук-обёртка с проверкой наличия провайдера
export function useAuth(): AuthContextValue {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error('useAuth должен использоваться внутри AuthProvider');
  return ctx;
}

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<string | null>(null);

  const login = (name: string) => setUser(name);
  const logout = () => setUser(null);

  // useMemo предотвращает создание нового объекта value на каждый рендер
  const value = React.useMemo(
    () => ({ user, login, logout }),
    [user]
  );

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

// Использование в любом компоненте внутри AuthProvider
function UserGreeting() {
  const { user, logout } = useAuth();
  if (!user) return <span>Гость</span>;
  return (
    <div>
      <span>Привет, {user}!</span>
      <button onClick={logout}>Выйти</button>
    </div>
  );
}

Типичные ошибки

Хранение всего приложения в одном контексте, что приводит к лишним ре-рендерам

Передача нового объекта в value без useMemo — провайдер пересоздаёт объект на каждый рендер родителя

Использование Context для часто меняющихся данных (например, позиция курсора) вместо более подходящих инструментов

Путаница между дефолтным значением createContext и значением провайдера — дефолт нужен только при отсутствии провайдера в дереве

Игнорирование разделения контекста на «стейт» и «диспатч», что заставляет ненужные компоненты перерисовываться

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее