useContext: суть, хук доступу до контексту та провайдери в React

Автор: IT Sectr Опубліковано: 2026-07-04 Час читання: 9 хв

useContext — це хук React, який надає функціональним компонентам прямий доступ до даних із контексту, створеного через createContext. Контекст у React вирішує проблему props drilling — передавання пропсів через безліч проміжних компонентів, які самі не використовують ці дані. За даними React Documentation (2025), useContext приймає об’єкт контексту та повертає поточне значення, встановлене найближчим Provider вище в дереві компонентів. При зміні значення в Provider усі компоненти, що використовують useContext, автоматично перемальовуються.

Головне

  • useContext — хук для читання значення з контексту React без props drilling.
  • createContext — створює об’єкт контексту зі значенням за замовчуванням і компонентом Provider.
  • Provider — компонент-обгортка, що передає значення контексту всім дочірнім елементам.
  • Перемалювання — зміна значення Provider викликає перемалювання всіх споживачів контексту.
  • Пропуск проміжних компонентів — useContext дозволяє передавати дані через кілька рівнів вкладеності.

Що таке useContext у React

useContext — це хук, доданий у React 16.8 разом з іншими хуками, який дозволяє читати значення з контексту React. Контекст — це механізм, вбудований у React, призначений для передавання даних через дерево компонентів без необхідності передавати пропси на кожному рівні вручну. useContext замінює компонент Consumer зі старого Context API та робить код більш лаконічним і читабельним.

Типові сценарії використання контексту включають теми оформлення (світла/темна), локаль і переклади (i18n), автентифікацію користувача, налаштування додатку та будь-які інші глобальні дані, які потрібні багатьом компонентам на різних рівнях вкладеності. Команда React рекомендує використовувати контекст для даних, які є глобальними для піддерева компонентів, але не для всього додатку.

За даними React Team — Документація контексту (2025), неправильне використання контексту — одна з головних причин проблем із продуктивністю в React-додатках. Кожна зміна значення в Provider викликає перемалювання всіх споживачів, незалежно від того, яка частина даних змінилася. Оптимізація через useMemo та розділення контекстів вирішує цю проблему.

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

// Створити контекст зі значенням за замовчуванням
const ThemeContext = createContext('світла');

function ThemedButton() {
    const theme = useContext(ThemeContext);
    return <button className={`btn-${theme}`}>Click</button>;
}

Як працює контекст у React

Механізм контексту в 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-компоненти можна вкладати один в одного, створюючи ієрархію контекстів. Кожен дочірній Provider перевизначає значення батьківського для свого піддерева. Це корисно, коли на одному екрані потрібна світла тема, а на вкладеному модальному вікні — темна. useContext завжди повертає значення найближчого Provider вгору по дереву.

jsx
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

Функція createContext(defaultValue) створює об’єкт контексту. Параметр defaultValue використовується, коли компонент викликає useContext, але вище в дереві немає відповідного Provider. Без defaultValue useContext поверне undefined, що може призвести до несподіваних помилок. Рекомендується завжди передавати осмислене значення за замовчуванням або null.

Створення кастомного провайдера — поширений патерн для інкапсуляції логіки контексту. Усередині такого провайдера зберігається стан (через useState або useReducer) і надається через value проп Provider. Це дозволяє приховати деталі реалізації від компонентів-споживачів і централізувати логіку керування контекстом в одному місці.

jsx
// Кастомний провайдер з керуванням станом
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 у дочірніх компонентах

У функціональних компонентах useContext — єдиний спосіб отримати доступ до контексту. Він замінює компонент Consumer зі старого Context API, який вимагав патерну render-prop: <ThemeContext.Consumer>{value => ...}</ThemeContext.Consumer>. useContext робить код більш лінійним і читабельним, особливо при роботі з кількома контекстами в одному компоненті.

При використанні кількох контекстів в одному компоненті просто викликай useContext кілька разів для кожного контексту. Кожен виклик повертає значення відповідного Provider. Порядок викликів не важливий, оскільки кожен контекст — це незалежна сутність. React оптимізує множинні виклики через ту саму систему fiber-посилань.

jsx
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 vs Redux: коли що вибирати

Вибір між useContext і Redux залежить від масштабу та складності керування станом. useContext + useReducer — легка заміна Redux для невеликих і середніх додатків. Вона не вимагає встановлення зовнішньої бібліотеки, простіша у вивченні та достатня для більшості завдань. Redux виправданий, коли потрібна сувора архітектура з middleware, девтулзами та імутабельними оновленнями.

Основна перевага Redux перед useContext — оптимізація перемалювань. За замовчуванням при зміні значення в Provider перемальовуються всі споживачі контексту. Redux з useSelector і shallowEqual дозволяє компонентам підписуватися лише на певні частини стану, що суттєво знижує кількість перемалювань у великих додатках. Контекст теж можна оптимізувати через розділення на безліч маленьких контекстів.

КритерійuseContextRedux
СкладністьНемає зовнішніх залежностейВимагає налаштування store і middleware
ПеремалюванняУсі споживачі при будь-якій змініЛише підписані на конкретний slice
DevToolsReact DevToolsRedux 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 і повинен використовуватися усвідомлено.

Типові помилки з useContext

Найпоширеніша помилка — перестворення value-об’єкта на кожен рендер Provider. Якщо передавати в Provider value={{ user, login }}, то при кожному рендері Provider створюється новий об’єкт, що викликає перемалювання всіх споживачів, навіть якщо дані не змінилися. Рішення — мемоізувати value через useMemo або використовувати окремі контексти для даних, що часто та рідко змінюються.

  • Зайві перемалювання — новий об’єкт value на кожен рендер Provider. Використовуй useMemo для мемоізації value.
  • Занадто великий контекст — один Provider з десятками полів змушує всі дочірні компоненти перемальовуватися при зміні будь-якого поля. Розділи на кілька контекстів за змістом.
  • Відсутність defaultValue — якщо Provider не знайдено, useContext поверне defaultValue, а якщо він undefined — кожен виклик буде падати з TypeError.
  • Вкладені Provider одного типу — перевизначення контексту на глибоких рівнях може заплутати та призвести до несподіваних значень.
jsx
// ❌ Новий об'єкт на кожен рендер — усі споживачі перемальовуються
<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 чи Redux?

Для простих сценаріїв useContext швидший через відсутність overhead від store і middleware. Але при частих оновленнях і великій кількості споживачів Redux виграє, оскільки його селектори (useSelector) підписуються на конкретні частини стану, а useContext перемальовує всіх споживачів при будь-якій зміні. Для додатків із високою частотою оновлень (анімації, real-time) варто вибирати Redux або спеціалізовані бібліотеки.

Чи можна використовувати useContext поза React-компонентом?

Ні. useContext, як і всі хуки, може викликатися лише всередині функціонального компонента React або кастомного хука. Якщо потрібно отримати значення контексту у звичайній функції (наприклад, в утиліті чи сервісі), передавай його параметром із компонента або використовуй окремий модуль із глобальним станом поза React.

Як працює useContext з TypeScript?

Типізація контексту в TypeScript — зазначення типу в createContext: createContext<AuthContextType | null>(null). Це гарантує, що useContext(AuthContext) повертає значення правильного типу. Зручний патерн — створення кастомного хука useAuth, який викликає useContext, перевіряє на null і викидає зрозумілу помилку: «useAuth повинен використовуватися всередині AuthProvider».

Чому useContext повертає undefined, хоча Provider є?

Найчастіша причина — компонент-споживач знаходиться не всередині відповідного Provider. Перевір, що Provider обертає все піддерево, в якому використовується useContext. Друга причина — Provider передано іншому об’єкту контексту: розробник створює контекст викликом createContext, а використовує useContext з іншим екземпляром createContext.

Підсумки

  • useContext — хук для читання значення з контексту React, що усуває необхідність у props drilling.
  • createContext — створює об’єкт контексту з Provider для передавання даних і defaultValue для випадку без Provider.
  • Мемоізація value — використовуй useMemo для value в Provider, щоб уникнути зайвих перемалювань споживачів.
  • Розділення контекстів — розбивай глобальний стан на кілька маленьких контекстів за логічними групами.
  • Provider ієрархія — можна вкладати Provider одного типу для перевизначення значення в частині дерева.
  • useContext + useReducer — легка заміна Redux для середніх додатків без external dependencies.
  • Не замінює Redux — для складної логіки з middleware і частими оновленнями вибирай Redux із селекторами.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також