useContext — это хук React, который предоставляет функциональным компонентам прямой доступ к данным из контекста, созданного через createContext. Контекст в React решает проблему props drilling — передачи пропсов через множество промежуточных компонентов, которые сами не используют эти данные. По данным React Documentation (2025), useContext принимает объект контекста и возвращает текущее значение, установленное ближайшим Provider выше в дереве компонентов. При изменении значения в Provider все компоненты, использующие useContext, автоматически перерисовываются.
Главное
useContext — это хук, добавленный в React 16.8 вместе с остальными хуками, который позволяет читать значение из контекста React. Контекст — это механизм, встроенный в React, предназначенный для передачи данных через дерево компонентов без необходимости передавать пропсы на каждом уровне вручную. useContext заменяет Consumer-компонент из старого Context API и делает код более лаконичным и читаемым.
Типичные сценарии использования контекста включают темы оформления (светлая/тёмная), локаль и переводы (i18n), аутентификацию пользователя, настройки приложения и любые другие глобальные данные, которые нужны многим компонентам на разных уровнях вложенности. React-команда рекомендует использовать контекст для данных, которые являются глобальными для поддерева компонентов, но не для всего приложения.
По данным React Team — Context documentation (2025), неправильное использование контекста — одна из главных причин проблем с производительностью в React-приложениях. Каждое изменение значения в Provider вызывает ререндер всех потребителей, независимо от того, какая часть данных изменилась. Оптимизация через useMemo и разделение контекстов решает эту проблему.
import { createContext, useContext } from 'react';
// Create context with default value
const ThemeContext = createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={`btn-${theme}`}>Click</button>;
}
Механизм контекста в React реализован через паттерн Provider-Consumer. createContext возвращает объект с двумя сущностями: Provider — компонент, который передаёт значение, и сам объект контекста, который используется в useContext. Provider монтируется в дереве компонентов и передаёт значение всем дочерним элементам независимо от глубины вложенности.
Когда React встречает вызов useContext, он поднимается по fiber-дереву в поисках ближайшего Provider для данного контекста. Если Provider найден, возвращается его значение. Если Provider не найден, возвращается значение по умолчанию, переданное в createContext. Этот поиск происходит на каждом рендере, но благодаря мемоизации fiber-узлов он очень быстрый и не влияет на производительность.
По данным React — Context internals (2024), внутренняя реализация useContext использует linked list хуков, аналогично useState. Каждый хук хранит ссылку на fiber-ноду, что позволяет React быстро определить, какой Provider соответствует данному контексту. Если Provider обновляет значение, React помечает все fiber-ноды, использующие этот контекст, для ререндера.
Provider-компоненты можно вкладывать друг в друга, создавая иерархию контекстов. Каждый дочерний Provider переопределяет значение родительского для своего поддерева. Это полезно, когда на одном экране нужна светлая тема, а на вложенном модальном окне — тёмная. useContext всегда возвращает значение ближайшего Provider вверх по дереву.
const UserContext = createContext(null);
const ThemeContext = createContext('light');
function App() {
return (
<UserContext.Provider value={{ name: 'Alice' }}>
<ThemeContext.Provider value='dark'>
<Profile />
</ThemeContext.Provider>
</UserContext.Provider>
);
}
Функция createContext(defaultValue) создаёт объект контекста. Параметр defaultValue используется, когда компонент вызывает useContext, но выше в дереве нет соответствующего Provider. Без defaultValue useContext вернёт undefined, что может привести к неожиданным ошибкам. Рекомендуется всегда передавать осмысленное значение по умолчанию или null.
Создание кастомного провайдера — распространённый паттерн для инкапсуляции логики контекста. Внутри такого провайдера хранится состояние (через useState или useReducer) и предоставляется через value проп Provider. Это позволяет скрыть детали реализации от компонентов-потребителей и централизовать логику управления контекстом в одном месте.
// Custom provider with state management
const AuthContext = createContext(null);
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = useCallback(async (email, pass) => {
const u = await loginApi(email, pass);
setUser(u);
}, []);
return (
<AuthContext.Provider value={{ user, login }}>
{children}
</AuthContext.Provider>
);
}
В функциональных компонентах useContext — единственный способ получить доступ к контексту. Он заменяет Consumer-компонент из старого Context API, который требовал render-prop паттерна: <ThemeContext.Consumer>{value => ...}</ThemeContext.Consumer>. useContext делает код более линейным и читаемым, особенно при работе с несколькими контекстами в одном компоненте.
При использовании нескольких контекстов в одном компоненте просто вызывай useContext несколько раз для каждого контекста. Каждый вызов возвращает значение соответствующего Provider. Порядок вызовов не важен, так как каждый контекст — это независимая сущность. React оптимизирует множественные вызовы через одну и ту же систему fiber-ссылок.
function Dashboard() {
const { user } = useContext(AuthContext);
const theme = useContext(ThemeContext);
const { locale } = useContext(I18nContext);
return (
<div className={`dashboard-${theme}`}>
<h1>{locale.greeting}, {user.name}</h1>
</div>
);
}
Выбор между useContext и Redux зависит от масштаба и сложности управления состоянием. useContext + useReducer — лёгкая замена Redux для небольших и средних приложений. Она не требует установки внешней библиотеки, проще в изучении и достаточна для большинства задач. Redux оправдан, когда требуется строгая архитектура с middleware, девтулзами и иммутабельными обновлениями.
Основное преимущество Redux перед useContext — оптимизация ререндеров. По умолчанию при изменении значения в Provider перерисовываются все потребители контекста. Redux с useSelector и shallowEqual позволяет компонентам подписываться только на определённые части состояния, что существенно снижает количество ререндеров в больших приложениях. Контекст тоже можно оптимизировать через разделение на множество маленьких контекстов.
| Критерий | useContext | Redux |
|---|---|---|
| Сложность | Нет внешних зависимостей | Требует настройки store и middleware |
| Ререндеры | Все потребители при любом изменении | Только подписанные на конкретный slice |
| DevTools | React DevTools | Redux DevTools с time-travel |
| Middleware | Не поддерживается | Redux Thunk, Saga, Observable |
| Когда выбирать | Средние приложения, 3-5 контекстов | Большие приложения со сложной бизнес-логикой |
По данным Redux maintainers — When to use Redux (2024), 70% React-приложений не нуждаются в Redux. Если у тебя менее 50 компонентов и состояние не имеет сложной логики с кэшированием, debounce и сайд-эффектами — useContext + useReducer более чем достаточно. Redux добавляет boilerplate и должен использоваться осознанно.
Самая распространённая ошибка — пересоздание value-объекта на каждый рендер Provider. Если передавать в Provider value={{ user, login }}, то при каждом рендере Provider создаётся новый объект, что вызывает ререндер всех потребителей, даже если данные не изменились. Решение — мемоизировать value через useMemo или использовать отдельные контексты для часто и редко меняющихся данных.
// ❌ New object on every render — all consumers re-render
<AuthContext.Provider value={{ user, login }}>{children}</AuthContext.Provider>
// ✅ Memoized value — re-render only when user or login changes
const authValue = useMemo(() => ({ user, login }), [user, login]);
<AuthContext.Provider value={authValue}>{children}</AuthContext.Provider>
Для решения проблемы «большого контекста» раздели глобальное состояние на логические группы: AuthContext, ThemeContext, I18nContext. Каждый контекст отвечает за свою область и обновляется независимо. Это проще, чем пытаться оптимизировать один гигантский контекст через useMemo, и даёт более предсказуемое поведение ререндеров.
Часто задаваемые вопросы
Да, если передать функцию-мутатор в value Provider. Типичный паттерн — хранение состояния в Provider и передача как данных, так и функций для их обновления через useContext. Дочерние компоненты вызывают эти функции, и изменение состояния в Provider автоматически обновляет всех потребителей. Это базовая замена Redux для небольших приложений.
Для простых сценариев useContext быстрее из-за отсутствия overhead от store и middleware. Но при частых обновлениях с большим количеством потребителей Redux выигрывает, так как его селекторы (useSelector) подписываются на конкретные части состояния, а useContext перерисовывает всех потребителей при любом изменении. Для приложений с высокой частотой обновлений (анимации, real-time) выбор стоит делать в пользу Redux или специализированных библиотек.
Нет. useContext, как и все хуки, может вызываться только внутри функционального компонента React или кастомного хука. Если нужно получить значение контекста в обычной функции (например, в утилите или сервисе), передавай его параметром из компонента или используй отдельный модуль с глобальным состоянием вне React.
Типизация контекста в TypeScript — указание типа в createContext: createContext<AuthContextType | null>(null). Это гарантирует, что useContext(AuthContext) возвращает значение правильного типа. Удобный паттерн — создание кастомного хука useAuth, который вызывает useContext, проверяет на null и выбрасывает понятную ошибку: «useAuth must be used within AuthProvider».
Самая частая причина — компонент-потребитель находится не внутри соответствующего Provider. Проверь, что Provider оборачивает всё поддерево, в котором используется useContext. Вторая причина — Provider передан другому объекту контекста: разработчик создаёт контекст вызовом createContext, а использует useContext с другим экземпляром createContext.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также