Der useContext-Hook liest den Wert eines Contexts in einer Function-Component. Anders als bei der <Context.Consumer>-Render-Prop-Form ist useContext direkt aufrufbar und liefert den Wert als normalen Rückgabewert. React findet den nächsten passenden Provider in der Komponenten-Hierarchie nach oben und liefert dessen value-Prop zurück. Gibt es keinen Provider, kommt der Default-Wert aus createContext(). useContext ist das Standard-Werkzeug gegen Prop Drilling — wenn ein Wert über viele Ebenen durchgereicht werden müsste, bietet Context den direkten Zugriff.
Signatur
const value = useContext(SomeContext);useContext nimmt das Context-Objekt (nicht den Provider), das mit createContext() erstellt wurde. Rückgabe: der aktuelle Context-Wert.
Grundlegendes Beispiel
import { createContext, useContext, useState } from 'react';
// 1. Context erstellen mit Default-Wert
const ThemeContext = createContext('light');
// 2. Provider in der App
export default function App() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
</ThemeContext.Provider>
);
}
// 3. Tief verschachteltes Kind liest den Wert
function Toolbar() {
return <ThemedButton />;
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={`btn-${theme}`}>Klick</button>;
}ThemedButton bekommt das Theme, ohne dass App → Toolbar → ThemedButton als Prop weiterreichen müssten.
Default-Wert ohne Provider
Wenn keine Provider-Komponente in der Hierarchie steht, liefert useContext den Default-Wert aus createContext(defaultValue).
const ThemeContext = createContext('light'); // Default ist 'light'
function Standalone() {
const theme = useContext(ThemeContext);
return <p>Theme: {theme}</p>; // → 'light' wenn kein Provider außen drum
}Praktisch für Komponenten, die in Tests oder Storybook ohne App-Setup laufen. Wichtig: das ist nicht „kein Wert", sondern der Default-Wert. Ein null als Default + Null-Check ist die idiomatische Form, wenn man „nicht in Provider" als Fehler behandeln will.
Custom-Hook-Wrapper
Der saubere Stil ist, den Context-Zugriff in einen Custom Hook zu wrappen — gibt klare API, optionalen Null-Check und vermeidet, dass Components direkt das Context-Objekt importieren müssen.
const ThemeContext = createContext(null);
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const ctx = useContext(ThemeContext);
if (ctx === null) {
throw new Error('useTheme must be inside <ThemeProvider>');
}
return ctx;
}
// Verwendung
function Header() {
const { theme, setTheme } = useTheme();
return <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>{theme}</button>;
}Der Null-Check zur Laufzeit fängt vergessene Provider-Wrapper sofort ab. In TypeScript ist der zusätzliche Vorteil, dass nach dem Check der Typ verfeinert ist.
Performance — alle Konsumenten rendern bei Value-Wechsel
Wenn der value-Prop des Providers eine neue Referenz bekommt, rendern alle Komponenten neu, die useContext für diesen Context aufrufen.
// FALLE: bei jedem Render entsteht ein NEUES Object — alle Konsumenten rendern
function Provider({ children }) {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
// BESSER: Object stabilisieren
function Provider({ children }) {
const [user, setUser] = useState(null);
const value = useMemo(() => ({ user, setUser }), [user]);
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);
}Auch besser: getrennte Contexts für selten/häufig wechselnde Werte. Ein ThemeContext (selten wechselnd) plus ein UserContext (öfter wechselnd) statt einem Mega-Context.
Wann Context, wann nicht?
| Use-Case | Empfehlung |
|---|---|
| Theme, Locale, Auth-User | Context |
| Form-State innerhalb eines Forms | Context (oder Form-Library) |
| Selten-wechselnde App-Konfiguration | Context |
| Häufig-wechselnde Werte (z.B. Cursor-Position) | NICHT Context — globaler Store |
| Server-State (API-Daten) | TanStack Query / SWR statt Context |
| Komplexer geteilter State + viele Updates | Redux / Zustand / Jotai |
Faustregel: Context ist für stabile, selten wechselnde Werte, die viele Komponenten brauchen. Für hochfrequente Updates ist eine State-Management-Library mit Selector-Mechanismus (Zustand, Jotai) effizienter.
Interessantes
useContext nimmt das Context-OBJEKT, nicht den Provider.
useContext(MyContext) ist richtig. useContext(MyContext.Provider) ist falsch — der Provider ist eine Komponente, nicht das Context-Objekt selbst.
Bei value-Wechsel rendern ALLE Konsumenten neu — auch React.memo greift nicht.
Context umgeht React.memo. Wer hochfrequent updatet, hat Performance-Probleme. Lösung: Context aufteilen (mehrere kleine), oder externe Stores mit Selector (Zustand, Jotai, Redux mit useSelector).
value-Prop bei jedem Render neu: useMemo benutzen.
<Provider value={{a, b}}> erzeugt jedes Mal ein neues Object — alle Konsumenten rendern. const v = useMemo(() => ({a, b}), [a, b]) stabilisiert.
Default-Wert ist NUR wenn kein Provider in der Hierarchie ist.
Wenn ein Provider existiert, gewinnt dessen value — auch wenn er undefined oder null setzt. Default ist nur Fallback für „außerhalb des Providers".
Custom-Hook-Wrapper ist die idiomatische Form.
Direkter Import von UserContext in jeder Konsumenten-Komponente ist Anti-Pattern — gibt Tight Coupling. Stattdessen einen useUser()-Hook exportieren, der intern useContext nutzt und Null-Checks macht.
React 19: statt (in Arbeit).
Die Kurzform <UserContext value={...}> ist in React 19 spezifiziert. Provider-Suffix bleibt rückwärtskompatibel.
useContext bricht die Hook-Regeln nicht — Top-Level wie andere Hooks.
Bedingte useContext-Aufrufe sind verboten. if (cond) useContext(...) ist Fehler. Bei „nur manchmal Context": immer aufrufen, im Rest-Logik conditional reagieren.
Mehrere Contexts: schachteln, nicht kombinieren.
<ThemeProvider><UserProvider><App /></UserProvider></ThemeProvider> — sauberer als alles in einen Mega-Context zu packen. Jeder Context hat seinen eigenen Update-Zyklus.