useContext — е React хук, който предоставя на функционалните компоненти преки достъп до данните от контекста, създаден чрез createContext. Контекстът в React решава проблема props drilling — пренасяне на props чрез много междинни компоненти, които самите не използват тези данни. Според React Documentation (2025), useContext приема обекта на контекста и връща текущата стойност, зададена от най-близкия Provider по-горе в дървото от компоненти. Когато стойността в Provider-а се промени, всички компоненти, използващи useContext, се преизрисоват автоматично.
Основни
useContext — е хук, добавен в React 16.8 заедно с останалите хукове, който позволява да четете стойност от React контекста. Контекстът е вграден механизъм в React, предназначен за пренасяне на данни чрез дървото от компоненти, без да се налага ръчно да пренасяте props на всяко ниво. useContext заменя Consumer компонента от стария Context API и прави кода по-кратък и по-четим.
Типични сценарии за използване на контекст включват дизайн теми (светла/тъмна), регионални настройки и преводи (i18n), удостовяване на потребители, настройки на приложението и всички други глобални данни, които са нужни на много компоненти на различни нива на вложеност. React екип препоръчва да използвате контекст за данни, които са глобални за поддървото от компоненти, но не за цялото приложение.
Според React Team — Context documentation (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 — Context internals (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>
// ✅ Мемоизирана стойност — преизрисоване само когато се промени потребителят или 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 е по-бърз поради липсата на допълнително натоварване от 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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също