Die Context API in React: Globaler State ohne Prop Drilling
Props durch fünf Komponenten-Ebenen durchreichen? Muss nicht sein. Mit der Context API stellst du Daten wie Theme oder User-Infos direkt dort bereit, wo sie gebraucht werden.
Wenn deine React-App wächst, kennst du das Problem vermutlich schon: Eine Information wie der eingeloggte User oder das aktuelle Farbschema wird ganz oben in der App verwaltet, aber ganz unten in einer verschachtelten Komponente gebraucht. Also reichst du die Daten als Props durch jede Ebene dazwischen – auch durch Komponenten, die damit gar nichts anfangen können. Dieses Muster nennt man Prop Drilling, und genau dafür hat React eine eingebaute Lösung: die Context API. In diesem Beitrag lernst du, wie du einen Context erstellst, Daten bereitstellst und sie in beliebig tief verschachtelten Komponenten ausliest.
Das Problem: Prop Drilling
Schauen wir uns zuerst an, wie das Problem in der Praxis aussieht. Hier wird ein user-Objekt durch zwei Zwischenkomponenten gereicht, die es selbst gar nicht benötigen:
function App() {
const user = { name: "Anna", role: "admin" };
return <Layout user={user} />;
}
function Layout({ user }) {
// Layout braucht user nicht, reicht es aber weiter
return <Sidebar user={user} />;
}
function Sidebar({ user }) {
// Sidebar braucht user auch nicht ...
return <UserBadge user={user} />;
}
function UserBadge({ user }) {
// ... erst hier wird user wirklich verwendet
return <span>Angemeldet als {user.name}</span>;
}Bei zwei Ebenen ist das noch erträglich. Bei fünf oder zehn Ebenen wird es unübersichtlich, fehleranfällig und mühsam zu refaktorisieren: Jede Komponente auf dem Weg muss die Prop kennen und weitergeben, obwohl sie inhaltlich nichts damit zu tun hat.
Die Lösung: createContext und der Provider
Die Context API besteht aus drei Bausteinen: Du erstellst einen Context mit createContext, stellst einen Wert über einen Provider bereit und liest ihn mit dem useContext-Hook aus – egal wie tief die Komponente im Baum sitzt.
import { createContext, useContext } from "react";
// 1. Context erstellen (gerne in einer eigenen Datei)
const UserContext = createContext(null);
function App() {
const user = { name: "Anna", role: "admin" };
// 2. Wert über den Provider bereitstellen
return (
<UserContext.Provider value={user}>
<Layout />
</UserContext.Provider>
);
}
function Layout() {
return <Sidebar />;
}
function Sidebar() {
return <UserBadge />;
}
function UserBadge() {
// 3. Wert direkt auslesen – ohne Props!
const user = useContext(UserContext);
return <span>Angemeldet als {user.name}</span>;
}Layout und Sidebar wissen jetzt nichts mehr vom User – sie sind wieder reine Struktur-Komponenten. Das Argument von createContext ist übrigens der Default-Wert: Er greift nur, wenn eine Komponente useContext aufruft, ohne dass ein passender Provider über ihr im Baum existiert.
Context mit State kombinieren
Richtig nützlich wird Context, wenn der bereitgestellte Wert sich ändern kann. Dafür kombinierst du ihn mit useState. Ein Klassiker ist ein Theme-Umschalter zwischen Hell- und Dunkelmodus:
import { createContext, useContext, useState } from "react";
const ThemeContext = createContext(null);
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme((current) => (current === "light" ? "dark" : "light"));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function ThemeButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button onClick={toggleTheme}>
Aktuell: {theme} – klicken zum Wechseln
</button>
);
}
function App() {
return (
<ThemeProvider>
<ThemeButton />
</ThemeProvider>
);
}Der Trick: Der Provider stellt nicht nur den aktuellen Wert bereit, sondern auch die Funktion zum Ändern. Jede Komponente unterhalb des Providers kann das Theme lesen und umschalten. Ändert sich der Wert, rendert React automatisch alle Komponenten neu, die den Context konsumieren.
Ein eigener Hook als saubere Schnittstelle
In der Praxis hat es sich bewährt, den Zugriff auf einen Context in einen eigenen Hook zu verpacken. So musst du useContext und das Context-Objekt nicht überall importieren und kannst gleich einen hilfreichen Fehler werfen, wenn der Provider vergessen wurde:
function useTheme() {
const context = useContext(ThemeContext);
if (context === null) {
throw new Error("useTheme muss innerhalb eines ThemeProvider verwendet werden");
}
return context;
}
// Verwendung in einer Komponente:
function Header() {
const { theme } = useTheme();
return <header className={theme === "dark" ? "header-dark" : "header-light"}>Meine App</header>;
}Dieses Muster – Provider-Komponente plus Custom Hook in einer Datei – ist der De-facto-Standard in vielen React-Projekten. Falls du Custom Hooks noch nicht kennst, lohnt sich ein Blick in unseren Beitrag zu eigenen Hooks in React.
Wann Context die richtige Wahl ist – und wann nicht
Context ist kein Ersatz für jeden State. Ein paar Faustregeln helfen bei der Entscheidung:
- Gut geeignet: Daten, die viele Komponenten in unterschiedlichen Teilen des Baums brauchen und die sich selten ändern – etwa Theme, Sprache, eingeloggter User oder Feature-Flags.
- Weniger geeignet: Lokaler State, den nur eine Komponente und ihre direkten Kinder brauchen. Hier sind normale Props klarer und einfacher zu testen.
- Vorsicht bei häufigen Updates: Ändert sich der Context-Wert oft (z. B. bei jeder Tastatureingabe), rendern alle konsumierenden Komponenten neu. Teile in solchen Fällen den Context lieber auf oder halte den State lokal.
- Erst Komposition prüfen: Manchmal löst schon das Durchreichen von
childrendas Problem, ganz ohne Context.
Für komplexe globale Zustände mit vielen Aktionen greifen größere Projekte oft zu Bibliotheken wie Zustand oder Redux – aber für die typischen Fälle wie Theme und User-Session ist die eingebaute Context API völlig ausreichend.
Fazit
Die Context API löst ein sehr konkretes Problem: Daten, die viele Komponenten brauchen, ohne Prop Drilling durch den ganzen Baum zu reichen. Das Rezept ist immer gleich – createContext erstellt den Context, ein Provider stellt den Wert bereit, useContext liest ihn aus. Kombiniert mit useState und einem Custom Hook erhältst du eine saubere, wiederverwendbare Lösung für globale Werte wie Theme oder User. Setze Context gezielt ein, wo er echten Mehrwert bringt, und bleib bei normalen Props, wo sie ausreichen – dann bleibt deine Komponentenstruktur übersichtlich und wartbar.