Menu

React useContext Hook: Daten ohne Props teilen

useContext liest einen Wert, den eine Elternkomponente bereitstellt, sodass eine tief verschachtelte Komponente ihn bekommt, ohne dass jede Komponente dazwischen ihn als Prop weiterreicht. Lerne createContext, Provider, das Aktualisieren von Context aus einem Kind und wie Context das erneute Rendern beeinflusst.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

useContext liest einen Wert, den eine Elternkomponente bereitstellt, sodass eine Komponente tief im Baum ihn bekommt, ohne dass jede Komponente dazwischen ihn als Prop weiterreicht. Du erzeugst einen Context mit createContext, umschließt einen Teil des Baums mit einem Provider mit value und rufst useContext(TheContext) überall dort auf, wo du diesen Wert brauchst.

Toolbar erwähnt das Theme nie, und doch ändern sich beide Buttons, wenn du das Kästchen anhakst. Genau darum geht es bei Context: Die Komponenten in der Mitte tragen keine Prop, die sie nicht nutzen, das Problem des Prop Drilling, das Context löst.

Die drei Schritte

// 1. Create it once, outside any component, usually in its own file
export const ThemeContext = createContext('light');

// 2. Provide a value to a part of the tree
<ThemeContext value={theme}>
    <Page />
</ThemeContext>

// 3. Read it in any component inside that part
const theme = useContext(ThemeContext);

In React 19 ist das Context-Objekt selbst der Provider. Älterer Code schreibt <ThemeContext.Provider value={theme}>, was weiterhin funktioniert, also wirst du beides sehen.

useContext sucht im Baum nach oben den nächsten Provider dieses Context. Eine Komponente liest den nächstgelegenen, also kannst du Provider verschachteln, um einen Wert für einen Abschnitt zu überschreiben.

Der Standardwert

Das Argument von createContext ist das, was useContext zurückgibt, wenn kein Provider über der Komponente steht. Es ändert sich nicht, wenn sich State ändert; es ist ein Fallback.

Das erste Greeting hat keinen Provider über sich und bekommt den Standardwert 'en'. Das zweite liest 'es', und das dritte liest das nähere 'ja'. Ändere das äußere value="es" zu value="ja", und nur die zweite Zeile ändert sich.

Context aus einem Kind aktualisieren

Context reicht einen Wert nur nach unten. Damit ein tiefes Kind ihn ändern kann, halte den Wert im State dort, wo der Provider ist, und lege den Setter daneben in den Context.

LoginButton ruft setUser auf, der State in App ändert sich, der Provider bekommt einen neuen Wert, und Header aktualisiert sich ebenfalls. Keine der Komponenten hat eine Prop bekommen.

Jeder Konsument rendert neu, wenn sich der Wert ändert

Wenn sich der value eines Providers ändert, rendert React jede Komponente, die useContext dafür aufruft, auch wenn sie in einem memo steckt und ihre Props gleich geblieben sind. React vergleicht alten und neuen Wert mit Object.is. Also ist value={{ user, setUser }} bei jedem Render von App ein neues Objekt, und jeder Konsument rendert erneut, selbst wenn sich user nicht geändert hat.

Beide Leser loggen beim ersten Render einmal. Klicke auf den Button: Nur PlainReader loggt erneut, weil sein Context ein neues Objekt bekommen hat. useMemo gibt MemoContext dasselbe Objekt, bis sich name ändert, also bleibt MemoReader, wie er ist. Das ist erst wichtig, wenn das Rendern langsam wird; für eine kleine App ist ein neues Objekt pro Render in Ordnung.

Contexts aufteilen

Ein Wert, der sich oft ändert, und ein Wert, der sich nie ändert, sollten sich keinen Context teilen. Wenn der Setter beim State liegt, rendert eine Komponente, die nur setUser aufrufen muss, trotzdem jedes Mal, wenn sich user ändert. Lege sie in zwei Contexts, und jeder Konsument abonniert nur, was er liest:

const UserContext = createContext(null);
const SetUserContext = createContext(() => {});

function UserProvider({ children }) {
    const [user, setUser] = useState(null);
    return (
        <SetUserContext value={setUser}>
            <UserContext value={user}>{children}</UserContext>
        </SetUserContext>
    );
}

setUser aus useState behält für immer dieselbe Identität, also rendern Komponenten, die nur SetUserContext lesen, nie wegen eines Logins. Dieselbe Idee gilt für unabhängige Daten: Ein Theme und ein Warenkorb gehören in getrennte Contexts, nicht in einen großen AppContext.

Ein häufiger letzter Schliff ist ein Custom Hook wie useUser(), der useContext(UserContext) aufruft und einen klaren Fehler wirft, wenn der Provider fehlt, damit Aufrufer das Context-Objekt nie direkt importieren.

Context, Props oder eine State-Bibliothek

  • Props sind der Standard. Einen Wert zwei oder drei Ebenen nach unten zu reichen ist klar und leicht nachzuverfolgen.
  • Context passt zu Werten, die viele Komponenten in vielen Tiefen brauchen und die sich selten ändern: Theme, Sprache, der angemeldete Nutzer, ein Objekt mit Feature-Flags.
  • Eine State-Bibliothek (Redux Toolkit, Zustand, Jotai und andere) passt zu großem State, der sich oft ändert, wenn eine Komponente einen Ausschnitt abonnieren und Renders durch den Rest überspringen soll. Context hat keinen solchen Selektor: Ein Konsument rendert bei jeder Änderung des Werts.

Bevor du zu Context oder einer Bibliothek greifst, prüfe, ob das Übergeben von Komponenten als children das Drilling beseitigt. Oft ist das so, ganz ohne neue API.

Häufig gestellte Fragen

Was macht useContext in React?

Es gibt den aktuellen Wert eines Context zurück, genommen vom nächsten Provider über der Komponente im Baum. Wenn sich der Wert dieses Providers ändert, rendert jede Komponente, die ihn liest, erneut.

Wofür ist der Standardwert in createContext?

Er ist das, was useContext zurückgibt, wenn kein Provider über der Komponente steht. Er ist nützlich für Tests und für Komponenten, die allein gerendert werden, und er ändert sich nie.

Brauche ich in React 19 noch Context.Provider?

Nein. In React 19 kannst du den Context selbst als Provider rendern: <ThemeContext value="dark">. <ThemeContext.Provider value="dark"> funktioniert weiterhin, also läuft älterer Code weiter.

Wie aktualisiere ich einen Context-Wert aus einer Kindkomponente?

Halte den Wert im State der Komponente, die den Provider rendert, und lege den Setter neben den Wert in den Context: value={{ theme, setTheme }}. Das Kind liest setTheme mit useContext und ruft es auf.

Ist React Context ein Ersatz für Redux?

Context reicht einen Wert den Baum hinunter; er verwaltet keinen State von selbst. Zusammen mit useState oder useReducer deckt er viele Apps ab. Eine State-Bibliothek bringt, was Context fehlt, etwa das Abonnieren eines einzelnen Ausschnitts, damit unabhängige Updates eine Komponente nicht neu rendern, plus DevTools und Middleware.

Illustration der Programmiersprachen bei Coddy

Lerne mit Coddy zu programmieren

LOS GEHT'S