Что такое context API и когда его использовать?
Context API — это встроенный механизм React, предназначенный для передачи данных между компонентами, минуя промежуточные уровни дерева. Он позволяет избегать «prop drilling», то есть ситуации, когда одни и те же props необходимо передавать через несколько уровней вложенных компонентов только для того, чтобы они дошли до нужного компонента внизу иерархии. Context предоставляет способ создания глобальных данных, которые могут быть доступны в любом компоненте дерева, если он находится внутри соответствующего провайдера.
Как устроен Context API
Context API состоит из трёх ключевых частей:
-
React.createContext() — создание объекта контекста.
-
<Context.Provider> — компонент, предоставляющий данные потомкам.
-
useContext(Context) — хук для получения доступа к данным из контекста.
Пример создания и использования контекста:
import React, { createContext, useContext, useState } from 'react';
// создаём контекст
const ThemeContext = createContext();
// провайдер контекста
const ThemeProvider = ({ children }) => {
const \[theme, setTheme\] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
// компонент-потребитель
const ThemeButton = () => {
const { theme, setTheme } = useContext(ThemeContext);
return (
<Button
title={\`Тема: ${theme}\`}
onPress={() => setTheme(theme === 'light' ? 'dark' : 'light')}
/>
);
};
// использование
const App = () => (
<ThemeProvider>
<ThemeButton />
</ThemeProvider>
);
Где применяют Context API
Context удобно использовать, когда необходимо предоставить доступ к общим данным нескольким компонентам, особенно если эти компоненты находятся на разных уровнях вложенности. Наиболее частые сценарии:
-
Текущий пользователь (auth user)
-
Тема оформления (светлая/тёмная)
-
Язык интерфейса (i18n)
-
Настройки приложения
-
Корзина покупок
-
Данные с сервера, актуальные для многих компонентов
Когда использовать, а когда — нет
Context удобен, но не универсален. Он не предназначен для частых изменений или хранения больших объёмов данных, особенно если компоненты, использующие контекст, могут часто перерендериваться.
Использовать Context стоит, если:
-
Данные редко меняются (например, тема оформления или язык)
-
Компоненты находятся глубоко в дереве, и props становятся неэффективными
-
Не требуется глобальное состояние на уровне всего приложения (как в Redux или Zustand)
Лучше не использовать Context, если:
-
Данные изменяются часто (например, инпуты в реальном времени, состояние загрузки, список товаров)
-
Требуется сложная логика управления состоянием (например, асинхронные действия, мидлвары, кэш)
-
Нужно следить за производительностью — каждый потребитель будет перерисован при изменении контекста
Поведение при изменении значений
Когда значение в Provider меняется, все компоненты-потребители контекста перерисовываются, даже если они используют только часть данных. Это может привести к проблемам с производительностью.
Решение:
-
Разделить контексты: вместо одного большого AppContext использовать несколько маленьких (например, ThemeContext, AuthContext, SettingsContext)
-
Мемоизировать значения в Provider, особенно если они содержат функции:
const value = useMemo(() => ({ theme, setTheme }), \[theme\]);
<ThemeContext.Provider value={value}>
Альтернатива Context API
Для более масштабных задач можно использовать:
-
Redux
-
MobX
-
Zustand
-
Recoil
-
Jotai
Они предлагают более эффективные механизмы подписки, селекторы и поддержку асинхронных операций.
Однако во многих приложениях Context оказывается вполне достаточным, особенно если его использовать локально — в рамках отдельного модуля, а не всего приложения.
Советы по использованию
-
Выносите контексты в отдельные модули для читаемости и повторного использования
-
Используйте useMemo и useCallback для минимизации лишних перерендеров
-
Не создавайте сложные вложенные контексты без необходимости
-
Используйте контексты совместно с кастомными хуками, чтобы инкапсулировать логику
Пример кастомного хука:
const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) throw new Error('useTheme must be used within ThemeProvider');
return context;
};
Такой подход делает код более надёжным и читаемым.