Zum Inhalt springen
React Anfänger 30 min

useContext Hook

Verstehe den useContext Hook für globale Daten in React. Teile Daten wie Themes, User-Info und Einstellungen über die gesamte App.

Aktualisiert:

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 fuerVerwende NICHT Context fuer
Theme (Dark/Light Mode)Lokaler Komponentenstate
Benutzer-AuthentifizierungDaten, die nur 1-2 Ebenen weitergereicht werden
Sprach-EinstellungenHoch-frequente Updates (z.B. Mausposition)
Warenkorb-DatenAlles, was in einer Komponente bleiben kann

Uebungen

  1. Erstelle einen Theme-Context mit Light/Dark-Mode und nutze ihn in mindestens 3 Komponenten
  2. Baue einen einfachen Auth-Context mit Login/Logout-Funktionalitaet
  3. 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 mit Provider, gelesen mit useContext()
  • 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!

Zurück zum React Kurs