useContext: istota, hook dostępu do kontekstu i providerzy w React

Autor: IT Sectr Opublikowano: 2026-07-04 Czas czytania: 9 min

useContext — to hook React, który zapewnia funkcjonalnym komponentom bezpośredni dostęp do danych z kontekstu utworzonego przez createContext. Kontekst w React rozwiązuje problem props drilling — przekazywania propsów przez wiele pośrednich komponentów, które same nie używają tych danych. Według React Documentation (2025), useContext przyjmuje obiekt kontekstu i zwraca bieżącą wartość ustawioną przez najbliższy Provider wyżej w drzewie komponentów. Gdy wartość w Providerze się zmieni, wszystkie komponenty używające useContext automatycznie przerenderowują się.

Najważniejsze

  • useContext — hook do odczytywania wartości z kontekstu React bez props drilling.
  • createContext — tworzy obiekt kontekstu z domyślną wartością i komponentem Provider.
  • Provider — komponent-opakowujący, przekazujący wartość kontekstu wszystkim elementom potomnym.
  • Przerenderowanie — zmiana wartości w Providerze powoduje przerenderowanie wszystkich konsumentów kontekstu.
  • Pomijanie pośrednich komponentów — useContext pozwala przekazywać dane przez kilka poziomów zagnieżdżenia.

Czym jest useContext w React

useContext — to hook dodany w React 16.8 wraz z pozostałymi hookami, który pozwala odczytywać wartość z kontekstu React. Kontekst to wbudowany w React mechanizm przeznaczony do przekazywania danych przez drzewo komponentów bez konieczności ręcznego przekazywania propsów na każdym poziomie. useContext zastępuje komponent Consumer ze starego Context API i sprawia, że kod jest bardziej zwięzły i Czytelny.

Typowe scenariusze użycia kontekstu obejmują motywy wyglądu (jasny/ciemny), ustawienia regionalne i tłumaczenia (i18n), uwierzytelnianie użytkownika, ustawienia aplikacji i wszelkie inne globalne dane, które są potrzebne wielu komponentom na różnych poziomach zagnieżdżenia. Zespół React zaleca używanie kontekstu dla danych, które są globalne dla poddrzewa komponentów, ale nie dla całej aplikacji.

Według React Team — Context documentation (2025), nieprawidłowe użycie kontekstu jest jedną z głównych przyczyn problemów z wydajnością w aplikacjach React. Każda zmiana wartości w Providerze powoduje przerenderowanie wszystkich konsumentów, niezależnie od tego, która część danych się zmieniła. Optymalizacja przez useMemo i podział kontekstów rozwiązuje ten problem.

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

// Utwórz kontekst z domyślną wartością
const ThemeContext = createContext('jasny');

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

Jak działa kontekst w React

Mechanizm kontekstu w React jest zaimplementowany przez wzorzec Provider-Consumer. createContext zwraca obiekt z dwoma bytami: Provider — komponent, który przekazuje wartość, oraz sam obiekt kontekstu używany w useContext. Provider montuje się w drzewie komponentów i przekazuje wartość wszystkim elementom potomnym niezależnie od głębokości zagnieżdżenia.

Gdy React napotyka wywołanie useContext, wędruje w górę drzewa fiber w poszukiwaniu najbliższego Providera dla danego kontekstu. Jeśli Provider zostanie znaleziony, zwracana jest jego wartość. Jeśli Provider nie zostanie znaleziony, zwracana jest wartość domyślna przekazana do createContext. To wyszukiwanie odbywa się przy każdym renderze, ale dzięki memizacji węzłów fiber jest bardzo szybkie i nie wpływa na wydajność.

Według React — Context internals (2024), wewnętrzna implementacja useContext używa połączonej listy hooków, podobnie jak useState. Każdy hook przechowuje odniesienie do węzła fiber, co pozwala React szybko określić, który Provider odpowiada danemu kontekstowi. Jeśli Provider aktualizuje wartość, React oznacza wszystkie węzły fiber używające tego kontekstu do przerenderowania.

Provider i zagnieżdżone konteksty

Komponenty Provider można zagnieżdżać jeden w drugim, tworząc hierarchię kontekstów. Każdy potomny Provider nadpisuje wartość rodzicielskiego dla swojego poddrzewa. Jest to przydatne, gdy na jednym ekranie potrzebny jest jasny motyw, a w zagnieżdżonym oknie modalnym — ciemny. useContext zawsze zwraca wartość najbliższego Providera w górę drzewa.

jsx
const UserContext = createContext(null);
const ThemeContext = createContext('jasny');

function App() {
    return (
        <UserContext.Provider value={{ name: 'Alice' }}>
            <ThemeContext.Provider value='dark'>
                <Profile />
            </ThemeContext.Provider>
        </UserContext.Provider>
    );
}

Tworzenie providera z createContext

Funkcja createContext(defaultValue) tworzy obiekt kontekstu. Parametr defaultValue jest używany, gdy komponent wywołuje useContext, ale wyżej w drzewie nie ma odpowiedniego Providera. Bez defaultValue useContext zwróci undefined, co może prowadzić do nieoczekiwanych błędów. Zaleca się zawsze przekazywać sensowną wartość domyślną lub null.

Tworzenie niestandardowego providera to powszechny wzorzec do enkapsulacji logiki kontekstu. Wewnątrz takiego providera przechowywany jest stan (przez useState lub useReducer) i udostępniany przez prop value w Providerze. Pozwala to ukryć szczegóły implementacji przed komponentami-konsumentami i scentralizować logikę zarządzania kontekstem w jednym miejscu.

jsx
// Niestandardowy provider z zarządzaniem stanem
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>
    );
}

Używanie useContext w komponentach potomnych

W komponentach funkcyjnych useContext jest jedynym sposobem na uzyskanie dostępu do kontekstu. Zastępuje on komponent Consumer ze starego Context API, który wymagał wzorca render-prop: <ThemeContext.Consumer>{value => ...}</ThemeContext.Consumer>. useContext sprawia, że kod jest bardziej liniowy i Czytelny, szczególnie przy pracy z wieloma kontekstami w jednym komponencie.

W przypadku używania wielu kontekstów w jednym komponencie po prostu wywołuj useContext kilka razy dla każdego kontekstu. Każde wywołanie zwraca wartość odpowiedniego Providera. Kolejność wywołań nie ma znaczenia, ponieważ każdy kontekst jest niezależną encją. React optymalizuje wielokrotne wywołania przez ten sam system odniesień 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: kiedy co wybrać

Wybór między useContext a Redux zależy od skali i złożoności zarządzania stanem. useContext + useReducer to lekka zamiana Redux dla małych i średnich aplikacji. Nie wymaga instalowania zewnętrznej biblioteki, jest łatwiejszy do nauczenia i wystarczający dla większości zadań. Redux jest uzasadniony, gdy wymagana jest ścisła architektura z middleware, narzędziami deweloperskimi i niemutowalnymi aktualizacjami.

Główną zaletą Redux nad useContext jest optymalizacja przerenderowań. Domyślnie przy zmianie wartości w Providerze przerenderowują się wszyscy konsumenci kontekstu. Redux z useSelector i shallowEqual pozwala komponentom subskrybować tylko określone części stanu, co znacząco zmniejsza liczbę przerenderowań w dużych aplikacjach. Kontekst również można zoptymalizować przez podział na wiele małych kontekstów.

KryteriumuseContextRedux
ZłożonośćBrak zewnętrznych zależnościWymaga konfiguracji store i middleware
PrzerenderowaniaWszyscy konsumenci przy każdej zmianieTylko subskrybujący konkretny slice
DevToolsReact DevToolsRedux DevTools z time-travel
MiddlewareNie jest wspieranyRedux Thunk, Saga, Observable
Kiedy wybraćŚrednie aplikacje, 3-5 kontekstówDuże aplikacje ze złożoną logiką biznesową

Według Redux maintainers — When to use Redux (2024), 70% aplikacji React nie potrzebuje Redux. Jeśli masz mniej niż 50 komponentów, a stan nie ma złożonej logiki z cache'owaniem, debounce i efektami ubocznymi — useContext + useReducer jest więcej niż wystarczający. Redux dodaje boilerplate i powinien być używany świadomie.

Typowe błędy z useContext

Najczęstszym błędem jest ponowne tworzenie obiektu value przy każdym renderze Providera. Jeśli przekazujesz do Providera value={{ user, login }}, to przy każdym renderze Providera tworzony jest nowy obiekt, co powoduje przerenderowanie wszystkich konsumentów, nawet jeśli dane się nie zmieniły. Rozwiązaniem jest memizacja value przez useMemo lub używanie oddzielnych kontekstów dla często i rzadko zmieniających się danych.

  • Zbędne przerenderowania — nowy obiekt value przy każdym renderze Providera. Używaj useMemo do memizacji value.
  • Zbyt duży kontekst — jeden Provider z dziesiątkami pól powoduje przerenderowanie wszystkich komponentów potomnych przy zmianie dowolnego pola. Podziel na kilka kontekstów według znaczenia.
  • Brak defaultValue — jeśli Provider nie zostanie znaleziony, useContext zwróci defaultValue, a jeśli jest undefined — każde wywołanie zakończy się błędem TypeError.
  • Zagnieżdżone Provider tego samego typu — nadpisywanie kontekstu na głębokich poziomach może dezorientować i prowadzić do nieoczekiwanych wartości.
jsx
// ❌ Nowy obiekt przy każdym renderze — wszyscy konsumenci przerenderowują się
<AuthContext.Provider value={{ user, login }}>{children}</AuthContext.Provider>

// ✅ Zamemorizowana wartość — przerenderowanie tylko gdy zmieni się użytkownik lub login
const authValue = useMemo(() => ({ user, login }), [user, login]);
<AuthContext.Provider value={authValue}>{children}</AuthContext.Provider>

Aby rozwiązać problem „dużego kontekstu”, podziel globalny stan na logiczne grupy: AuthContext, ThemeContext, I18nContext. Każdy kontekst odpowiada za swój obszar i aktualizuje się niezależnie. Jest to prostsze niż próba optymalizacji jednego gigantycznego kontekstu przez useMemo i daje bardziej przewidywalne zachowanie przerenderowań.

Często zadawane pytania

Czy można zmieniać kontekst z komponentu potomnego?

Tak, jeśli przekazesz funkcję-mutator w value Providera. Typowym wzorcem jest przechowywanie stanu w Providerze i przekazywanie zarówno danych, jak i funkcji do ich aktualizacji przez useContext. Komponenty potomne wywołują te funkcje, a zmiana stanu w Providerze automatycznie aktualizuje wszystkich konsumentów. Jest to podstawowa zamiana Redux dla małych aplikacji.

Co jest szybsze — useContext czy Redux?

Dla prostych scenariuszy useContext jest szybszy ze względu na brak narzutu ze strony store i middleware. Ale przy częstych aktualizacjach z dużą liczbą konsumentów Redux wygrywa, ponieważ jego selektory (useSelector) subskrybują konkretne części stanu, podczas gdy useContext przerenderowuje wszystkich konsumentów przy każdej zmianie. Dla aplikacji z wysoką częstotliwością aktualizacji (animacje, real-time) warto wybrać Redux lub wyspecjalizowane biblioteki.

Czy można używać useContext poza komponentem React?

Nie. useContext, podobnie jak wszystkie hooki, może być wywoływany tylko wewnątrz funkcyjnego komponentu React lub własnego hooka. Jeśli potrzebujesz uzyskać wartość kontekstu w zwykłej funkcji (np. w narzędziu lub serwisie), przekaz ją jako parametr z komponentu lub użyj osobnego modułu z globalnym stanem poza React.

Jak działa useContext z TypeScript?

Typowanie kontekstu w TypeScript polega na określeniu typu w createContext: createContext<AuthContextType | null>(null). Gwarantuje to, że useContext(AuthContext) zwraca wartość poprawnego typu. Wygodnym wzorcem jest utworzenie własnego hooka useAuth, który wywołuje useContext, sprawdza null i rzuca zrozumiały błąd: „useAuth must be used within AuthProvider”.

Dlaczego useContext zwraca undefined, mimo że Provider istnieje?

Najczęstszą przyczyną jest to, że komponent-konsument znajduje się poza odpowiednim Providerem. Sprawdź, czy Provider obejmuje całe poddrzewo, w którym używany jest useContext. Drugą przyczyną jest przekazanie Providera do innego obiektu kontekstu: programista tworzy kontekst przez wywołanie createContext, a używa useContext z inną instancją createContext.

Podsumowanie

  • useContext — hook do odczytywania wartości z kontekstu React, eliminujący potrzebę props drilling.
  • createContext — tworzy obiekt kontekstu z Providerem do przekazywania danych i defaultValue na wypadek braku Providera.
  • Memizacja value — używaj useMemo dla value w Providerze, aby uniknąć zbędnych przerenderowań konsumentów.
  • Podział kontekstów — dziel globalny stan na kilka małych kontekstów według grup logicznych.
  • Hierarchia Providerów — można zagnieżdżać Provider tego samego typu w celu nadpisania wartości w części drzewa.
  • useContext + useReducer — lekka zamiana Redux dla średnich aplikacji bez zewnętrznych zależności.
  • Nie zastępuje Redux — dla złożonej logiki z middleware i częstymi aktualizacjami wybieraj Redux z selektorami.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również