useContext verstehen: Prop Drilling vermeiden
useContext verstehen
~11 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Bisher haben alle unsere Beispiele Daten über Props von oben nach unten weitergegeben. Was aber, wenn ganz viele, weit voneinander entfernte Komponenten denselben Wert brauchen – zum Beispiel, ob ein Nutzer gerade angemeldet ist? Dafür gibt es Context.
Das Problem: "Prop Drilling"
Stellen Sie sich vor, App weiß, ob der Nutzer angemeldet ist, aber eine tief verschachtelte Komponente sechs Ebenen weiter unten (z. B. ein Logout-Knopf im Header) braucht diese Information. Ohne Context müssten Sie den Wert als Prop durch JEDE Zwischenkomponente durchreichen – auch durch solche, die den Wert selbst gar nicht brauchen, sondern ihn nur weitergeben. Das nennt man "Prop Drilling" und wird schnell unübersichtlich.
Einen Context erstellen: AuthContext
Legen Sie einen neuen Ordner src/context/ mit der Datei AuthContext.jsx an. Wir bauen einen einfachen Login-Zustand, den wir ab Kapitel 17 (Protected Routes) für einen geschützten Bereich verwenden:
import { createContext, useState } from 'react';
export const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
function login(username) {
setUser({ username });
}
function logout() {
setUser(null);
}
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}Drei Teile spielen hier zusammen: createContext(null) erzeugt den Context selbst (null ist nur der Startwert, bevor ein Provider ihn überschreibt). AuthProvider ist eine ganz normale Komponente, die ihren children-Prop (alles, was zwischen ihren eigenen öffnenden/schließenden Tags steht) in <AuthContext.Provider value={{...}}> einwickelt – JEDE Komponente innerhalb dieses Providers kann den value lesen, egal wie tief verschachtelt.
main.jsx aktualisieren: die ganze App umwickeln
Damit wirklich jede Komponente Zugriff hat, wickeln wir die gesamte App im Einstiegspunkt main.jsx ein:
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
import { AuthProvider } from './context/AuthContext.jsx';
import './index.css';
createRoot(document.getElementById('root')).render(
<StrictMode>
<AuthProvider>
<App />
</AuthProvider>
</StrictMode>
);App.jsx aktualisieren: useContext verwenden
Jetzt lesen wir den Context-Wert mit useContext. Ergänzen Sie in src/App.jsx den Import und eine kleine Anmeldeanzeige oben in der Seite:
import { useContext, useEffect, useMemo, useRef, useState } from 'react';
import ProductCard from './components/ProductCard';
import { AuthContext } from './context/AuthContext';
const SAMPLE_PRODUCTS = [
{ sku: 'schuhe-01', name: 'Wanderschuhe', price: 89.99, imageUrl: 'https://picsum.photos/seed/1/300' },
{ sku: 'rucksack-01', name: 'Trekking-Rucksack', price: 59.5, imageUrl: 'https://picsum.photos/seed/2/300' },
{ sku: 'jacke-01', name: 'Regenjacke', price: 74.0, imageUrl: 'https://picsum.photos/seed/3/300' },
];
function App() {
const { user, login, logout } = useContext(AuthContext);
const [query, setQuery] = useState('');
const searchInputRef = useRef(null);
useEffect(() => {
document.title = `Produktkatalog (${SAMPLE_PRODUCTS.length} Produkte)`;
searchInputRef.current.focus();
}, []);
const gefiltert = SAMPLE_PRODUCTS.filter((produkt) =>
produkt.name.toLowerCase().includes(query.toLowerCase())
);
const teuerstesProdukt = useMemo(() => {
return gefiltert.reduce(
(max, p) => (p.price > (max?.price ?? 0) ? p : max),
null
);
}, [gefiltert]);
return (
<div className="app">
<header className="app-header">
<h1>Produktkatalog</h1>
{user ? (
<p>Angemeldet als {user.username} <button onClick={logout}>Abmelden</button></p>
) : (
<button onClick={() => login('Max Mustermann')}>Anmelden</button>
)}
</header>
<input
ref={searchInputRef}
type="text"
placeholder="Produkt suchen..."
value={query}
onChange={(event) => setQuery(event.target.value)}
/>
{teuerstesProdukt && (
<p>Teuerstes Produkt: {teuerstesProdukt.name} ({teuerstesProdukt.price.toFixed(2)} €)</p>
)}
<div className="product-grid">
{gefiltert.map((produkt) => (
<ProductCard
key={produkt.sku}
name={produkt.name}
price={produkt.price}
imageUrl={produkt.imageUrl}
onSelect={() => alert(produkt.name + ' angeklickt')}
/>
))}
</div>
</div>
);
}
export default App;Projektstruktur nach diesem Kapitel
produktkatalog-web/
├── index.html
├── package.json
├── vite.config.js
└── src/
├── main.jsx (aktualisiert: AuthProvider)
├── App.jsx (aktualisiert: useContext)
├── index.css
├── context/
│ └── AuthContext.jsx ← NEU
└── components/
└── ProductCard.jsxlogin('Max Mustermann') ist hier nur eine Demo-Anmeldung ohne echte Prüfung – ein richtiges Login-Formular mit Validierung bauen wir in Kapitel 13–14, und einen wirklich geschützten Bereich in Kapitel 17. Für den Moment reicht das, um useContext in Aktion zu sehen: der Anmeldestatus ist überall verfügbar, ohne ihn als Prop durchzureichen.
Tipp: useContext ersetzt NICHT useState – es transportiert nur bereits vorhandenen State (aus dem Provider) an Stellen, die ihn brauchen, ohne Prop Drilling. Für rein lokalen Zustand innerhalb einer einzelnen Komponente bleibt useState die richtige Wahl.