useContext Hook
Verstehe den useContext Hook für globale Daten in React. Teile Daten wie Themes, User-Info und Einstellungen über die gesamte App.
useContext Hook
Der useContext-Hook loest ein haeufiges Problem in React: Daten ueber viele Komponentenebenen teilen, ohne sie als Props durch jede Zwischenkomponente zu reichen. Perfekt fuer Themes, Benutzerinformationen und App-Einstellungen.
Das Problem: Prop Drilling
Ohne Context musst du Props durch jede Ebene reichen:
// Prop Drilling - muehsam und fehleranfaellig!
function App() {
const [user, setUser] = useState({ name: 'Max', role: 'admin' });
return <Layout user={user} />; // Level 1
}
function Layout({ user }) {
return <Sidebar user={user} />; // Level 2
}
function Sidebar({ user }) {
return <UserMenu user={user} />; // Level 3
}
function UserMenu({ user }) {
return <span>Hallo, {user.name}!</span>; // Endlich benutzt!
}
Das ist muehsam, fehleranfaellig und macht Zwischenkomponenten unnoetig komplex.
Die Loesung: Context
Context macht Daten direkt verfuegbar, ohne sie durchzureichen:
Schritt 1: Context erstellen
import { createContext } from 'react';
const UserContext = createContext(null);
Schritt 2: Provider einrichten
function App() {
const [user, setUser] = useState({ name: 'Max', role: 'admin' });
return (
<UserContext.Provider value={user}>
<Layout />
</UserContext.Provider>
);
}
Schritt 3: Context verwenden
import { useContext } from 'react';
function UserMenu() {
const user = useContext(UserContext);
return <span>Hallo, {user.name}!</span>;
}
Keine Props mehr noetig – UserMenu greift direkt auf die Daten zu!
Praktisches Beispiel: Theme-System
Ein Dark/Light-Mode ist ein perfekter Anwendungsfall fuer Context:
// ThemeContext.jsx
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
function toggleTheme() {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
}
const value = { theme, toggleTheme };
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme muss innerhalb von ThemeProvider verwendet werden');
}
return context;
}
// App.jsx
import { ThemeProvider } from './ThemeContext';
function App() {
return (
<ThemeProvider>
<Header />
<MainContent />
</ThemeProvider>
);
}
// Header.jsx
import { useTheme } from './ThemeContext';
function Header() {
const { theme, toggleTheme } = useTheme();
return (
<header style={{
background: theme === 'dark' ? '#1a1a2e' : '#ffffff',
color: theme === 'dark' ? '#e0e0e0' : '#333333',
padding: '1rem'
}}>
<h1>Meine App</h1>
<button onClick={toggleTheme}>
{theme === 'dark' ? 'Light Mode' : 'Dark Mode'}
</button>
</header>
);
}
Auth-Context: Benutzer-Verwaltung
// AuthContext.jsx
import { createContext, useContext, useState } from 'react';
const AuthContext = createContext();
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
function login(email, password) {
// In der Praxis: API-Aufruf
setUser({ email, name: email.split('@')[0] });
}
function logout() {
setUser(null);
}
const value = {
user,
isLoggedIn: !!user,
login,
logout
};
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth muss innerhalb von AuthProvider verwendet werden');
}
return context;
}
// Verwendung in Komponenten
function NavBar() {
const { user, isLoggedIn, logout } = useAuth();
return (
<nav>
{isLoggedIn ? (
<>
<span>Hallo, {user.name}!</span>
<button onClick={logout}>Abmelden</button>
</>
) : (
<a href="/login">Anmelden</a>
)}
</nav>
);
}
Mehrere Contexts kombinieren
Du kannst beliebig viele Contexts verschachteln:
function App() {
return (
<AuthProvider>
<ThemeProvider>
<LanguageProvider>
<Router>
<AppContent />
</Router>
</LanguageProvider>
</ThemeProvider>
</AuthProvider>
);
}
Context mit useReducer
Fuer komplexeren State kombiniere Context mit useReducer:
import { createContext, useContext, useReducer } from 'react';
const CartContext = createContext();
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
const existing = state.find(item => item.id === action.payload.id);
if (existing) {
return state.map(item =>
item.id === action.payload.id
? { ...item, quantity: item.quantity + 1 }
: item
);
}
return [...state, { ...action.payload, quantity: 1 }];
case 'REMOVE_ITEM':
return state.filter(item => item.id !== action.payload);
case 'CLEAR':
return [];
default:
return state;
}
}
export function CartProvider({ children }) {
const [cart, dispatch] = useReducer(cartReducer, []);
const totalItems = cart.reduce((sum, item) => sum + item.quantity, 0);
const totalPrice = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
const value = { cart, totalItems, totalPrice, dispatch };
return (
<CartContext.Provider value={value}>
{children}
</CartContext.Provider>
);
}
export function useCart() {
return useContext(CartContext);
}
// Verwendung
function ProductCard({ product }) {
const { dispatch } = useCart();
return (
<div>
<h3>{product.name}</h3>
<p>{product.price} Euro</p>
<button onClick={() => dispatch({
type: 'ADD_ITEM',
payload: product
})}>
In den Warenkorb
</button>
</div>
);
}
function CartSummary() {
const { totalItems, totalPrice } = useCart();
return (
<div>
<span>Warenkorb: {totalItems} Artikel</span>
<span>Gesamt: {totalPrice.toFixed(2)} Euro</span>
</div>
);
}
Wann Context verwenden?
| Verwende Context fuer | Verwende NICHT Context fuer |
|---|---|
| Theme (Dark/Light Mode) | Lokaler Komponentenstate |
| Benutzer-Authentifizierung | Daten, die nur 1-2 Ebenen weitergereicht werden |
| Sprach-Einstellungen | Hoch-frequente Updates (z.B. Mausposition) |
| Warenkorb-Daten | Alles, was in einer Komponente bleiben kann |
Uebungen
- Erstelle einen Theme-Context mit Light/Dark-Mode und nutze ihn in mindestens 3 Komponenten
- Baue einen einfachen Auth-Context mit Login/Logout-Funktionalitaet
- Erstelle einen Notification-Context, der App-weite Benachrichtigungen verwaltet
Was kommt als Naechstes?
Du beherrschst jetzt useContext fuer globale Daten. Im naechsten Kapitel lernst du, wie du eigene Hooks erstellst – damit kannst du wiederverwendbare Logik aus Komponenten extrahieren.
Zusammenfassung
- Context loest das Prop-Drilling-Problem fuer globale Daten
- Erstelle Context mit
createContext(), bereitgestellt mitProvider, gelesen mituseContext() - Erstelle einen Custom Hook (z.B.
useTheme) fuer sauberen Zugriff - Kombiniere Context mit useReducer fuer komplexen State
- Nutze Context nur fuer wirklich globale Daten, nicht fuer lokalen State
Pro-Tipp: Erstelle fuer jeden Context einen eigenen Custom Hook (z.B. useAuth() statt useContext(AuthContext)). Das kapselt die Implementierung, ermoeglicht bessere Fehlermeldungen und macht den Code lesbarer!