useContext
Einen Wert nach unten durchreichen, ohne jede Zwischenstation zu nerven
Context ist eine Rohrpost durch deine Komponenten: oben etwas hineinlegen, unten überall herausnehmen – ohne es durch fünf Ebenen als Prop weiterzugeben.
Das Problem hat einen Namen: Prop DrillingWenn ein Wert durch mehrere Komponenten durchgereicht wird, die ihn selbst gar nicht brauchen.→. Du willst das Theme von ganz oben bis zum Button ganz unten bringen – und musst es durch vier Komponenten reichen, die es selbst gar nicht brauchen. Genau das löst Context. Und nichts anderes.
Props sind wie von Hand weiterreichen: jede Person in der Kette muss das Paket anfassen. ContextEine Art Rohrpost: Ein Wert wird oben eingelegt und kann überall weiter unten herausgeholt werden.→ ist die Rohrpost: einmal oben einwerfen, unten kommt es an – die Ebenen dazwischen merken nichts davon.
Drei Schritte
import { createContext, useContext, useState } from "react";
// 1) Die Rohrpost anlegen
const ThemeContext = createContext<Theme | null>(null);
// 2) Oben etwas hineinlegen
function App() {
const [mode, setMode] = useState("hell");
const wert = { mode, umschalten: () => setMode(m => m === "hell" ? "dunkel" : "hell") };
return (
<ThemeContext.Provider value={wert}>
<GanzeSeite />
</ThemeContext.Provider>
);
}
// 3) Unten herausnehmen – egal wie tief
function Button() {
const theme = useContext(ThemeContext);
return <button onClick={theme.umschalten}>{theme.mode}</button>;
} Statt an 30 Stellen useContext(ThemeContext) zu schreiben, schreibst du einmal useTheme(). Vorteil: Die Prüfung „gibt es überhaupt einen Provider?“ steht an einer Stelle, und du kannst innen später alles umbauen.
export function useTheme() {
const wert = useContext(ThemeContext);
if (!wert) throw new Error("useTheme braucht einen <ThemeProvider> darüber");
return wert;
} Wichtig: Context speichert nichts
Context transportiert nur. Der State lebt weiterhin in einem ganz normalen useState in der Komponente, die den ProviderDie Komponente, die einen Context-Wert bereitstellt. Alles, was in ihr steckt, kommt an den Wert heran.→ rendert. Context ersetzt also keinen State-Manager wie Redux – es ersetzt das Durchreichen von Props.
- +
Theme, Sprache, Formatierung
- +
Wer ist angemeldet und was darf die Person
- +
Werte innerhalb einer Komponentenfamilie (
<Tabs>→<Tab>) - +
Kurz: Dinge, die selten wechseln und die viele brauchen
- −
Werte, die sich ständig ändern (Mausposition, jeder Tastendruck)
- −
Als Zwischenspeicher für Serverdaten – dafür gibt es fertige Bibliotheken
- −
Um zwei Ebenen Props zu sparen – dann ist die Prop einfacher
Die eine Falle: das Objekt ist jedes Mal neu
function App() {
const [user, setUser] = useState(null);
// dieses Objekt ist bei JEDEM Zeichnen neu
return (
<UserContext.Provider value={{ user, setUser }}>
<GroßerBaum />
</UserContext.Provider>
);
} function App() {
const [user, setUser] = useState(null);
const wert = useMemo(() => ({ user, setUser }), [user]);
return (
<UserContext.Provider value={wert}>
<GroßerBaum />
</UserContext.Provider>
);
} Jede Komponente, die den Context liest, zeichnet neu, sobald der Wert ein anderer ist – und ein frisch gebautes Objekt ist immer ein anderes. useMemo lernst du in der Lektion zu useMemo & useCallback kennen.
▸ Genauer erklärt: Aufteilen, memo und Next.js optional
Ein bewährtes Muster: zwei Contexts – einen für die Daten (user), einen für die Aktionen (logout, setUser). Der Aktions-Context ändert sich nie, deshalb zeichnen Komponenten, die nur Aktionen brauchen, gar nicht mit.
Wichtig zu wissen: React.memo hilft gegen Context-Updates nicht. Der Wert kommt nicht über Props herein, sondern an ihnen vorbei – die Prop-Prüfung sieht ihn also gar nicht.
createContext und useContext funktionieren nur in einer Client-KomponenteEine Komponente, die im Browser läuft und deshalb State, Hooks und Klicks kann. Erkennbar an "use client" oben in der Datei.→. Der Provider gehört also in eine Datei mit "use client" ganz oben. Server-Komponenten darfst du weiterhin als children hineinreichen – die laufen dann trotzdem auf dem Server.
Statt <ThemeContext.Provider value={…}> darfst du inzwischen <ThemeContext value={…}> schreiben. Beides funktioniert.
Sitzt das schon?
4 Fragen zu dieser Lektion. Falsche Antworten landen in deiner Statistik.
Jetzt selbst schreiben
mode und umschalten werden durch drei Ebenen gereicht, obwohl nur die unterste sie braucht. Ersetze das durch einen Context mit eigenem Hook useTheme().
- □
ToolbarundPanelnehmen keine Props mehr entgegen - □
ThemedButtonholt sich alles überuseTheme() - □Ein Klick schaltet zwischen
hellunddunkelum
import { createContext, useContext, useState } from "react"; // TODO: Context anlegen und einen useTheme()-Hook bauen function ThemedButton({ mode, umschalten }: any) { return ( <button onClick={umschalten} data-testid="btn"> Modus: {mode} </button> ); } function Panel({ mode, umschalten }: any) { return <ThemedButton mode={mode} umschalten={umschalten} />; } function Toolbar({ mode, umschalten }: any) { return <Panel mode={mode} umschalten={umschalten} />; } export default function App() { const [mode, setMode] = useState("hell"); const umschalten = () => setMode((m) => (m === "hell" ? "dunkel" : "hell")); return ( <div style={{ fontFamily: "system-ui", padding: 16 }}> <Toolbar mode={mode} umschalten={umschalten} /> </div> ); }