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 — Документація контексту (2025), неправильне використання контексту — одна з головних причин проблем із продуктивністю в React-додатках. Кожна зміна значення в Provider викликає перемалювання всіх споживачів, незалежно від того, яка частина даних змінилася. Оптимізація через useMemo та розділення контекстів вирішує цю проблему.
import { createContext, useContext } from 'react';
// Створити контекст зі значенням за замовчуванням
const ThemeContext = createContext('світла');
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 — Внутрішній устрій контексту (2024), внутрішня реалізація useContext використовує зв’язаний список хунків, аналогічно до useState. Кожен хук зберігає посилання на fiber-вузол, що дозволяє React швидко визначити, який Provider відповідає даному контексту. Якщо Provider оновлює значення, React позначає всі fiber-вузли, що використовують цей контекст, для перемалювання.
Provider-компоненти можна вкладати один в одного, створюючи ієрархію контекстів. Кожен дочірній Provider перевизначає значення батьківського для свого піддерева. Це корисно, коли на одному екрані потрібна світла тема, а на вкладеному модальному вікні — темна. useContext завжди повертає значення найближчого Provider вгору по дереву.
const UserContext = createContext(null);
const ThemeContext = createContext('світла');
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. Це дозволяє приховати деталі реалізації від компонентів-споживачів і централізувати логіку керування контекстом в одному місці.
// Кастомний провайдер з керуванням станом
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 або використовувати окремі контексти для даних, що часто та рідко змінюються.
// ❌ Новий об'єкт на кожен рендер — усі споживачі перемальовуються
<AuthContext.Provider value={{ user, login }}>{children}</AuthContext.Provider>
// ✅ Мемоізоване значення — перемалювання лише коли user або login змінюється
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 повинен використовуватися всередині AuthProvider».
Найчастіша причина — компонент-споживач знаходиться не всередині відповідного Provider. Перевір, що Provider обертає все піддерево, в якому використовується useContext. Друга причина — Provider передано іншому об’єкту контексту: розробник створює контекст викликом createContext, а використовує useContext з іншим екземпляром createContext.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також