React Hooks sind Funktionen, die es ermöglichen, State, Effekte, Context und andere React-Funktionen in funktionalen Komponenten zu verwenden, ohne Klassen zu schreiben. Mit React 16.8 eingeführt, haben Hooks den Entwicklungsansatz radikal verändert und komplexe Patterns wie HOC und render props ersetzt. Laut React, 2024 gibt es 15 eingebaute Hooks, die alle Szenarien der Komponentenverwaltung abdecken.
Das Wichtigste
React Hooks sind eine Reihe von in React integrierten Funktionen, die es ermöglichen, sich in die internen Mechanismen des Frameworks einzuklinken, ohne Klassen zu erstellen. Vor React 16.8 konnten funktionale Komponenten nur Props akzeptieren und JSX zurückgeben — jede komplexe Logik erforderte eine Klassenkomponente.
Das Problem bei Klassenkomponenten ist die Aufteilung zusammengehöriger Logik. Code, der zu einer Entität gehört (z. B. ein WebSocket-Abonnement), wurde zwangsweise auf componentDidMount, componentDidUpdate und componentWillUnmount aufgeteilt. Hooks lösen dies: Die gesamte Logik wird in einem useEffect gruppiert.
Das zweite Problem ist die Wiederverwendung von Logik. Um Zustandslogik zwischen Klassenkomponenten wiederzuverwenden, musste man HOC (Higher-Order Components) oder render props anwenden — beide Patterns erzeugten zusätzliche Verschachtelung im Komponentenbaum. Benutzerdefinierte Hooks lösen dies ohne Verschachtelung.
useState ist der grundlegendste Hook. Er nimmt einen Anfangswert entgegen und gibt ein Tupel zurück: den aktuellen Wert und eine Funktion zu dessen Aktualisierung. Im Gegensatz zu this.setState in Klassen führt useState keine Objektzusammenführung durch — der neue Zustand ersetzt den vorherigen für einen bestimmten useState-Aufruf vollständig.
import React, { useState } from 'react';
function Form() {
const [text, setText] = useState('');
const [submitting, setSubmitting] = useState(false);
async function handleSubmit() {
setSubmitting(true);
await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify({ text })
});
setSubmitting(false);
}
return (
<div>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={handleSubmit}
disabled={submitting}>
{submitting ? 'Saving...' : 'Submit'}
</button>
</div>
);
}
Der Setter von useState kann eine Funktion akzeptieren, die den vorherigen Wert erhält und einen neuen zurückgibt. Die funktionale Form ist erforderlich, wenn der neue Zustand aus dem vorherigen berechnet wird und gebündelte setState-Aufrufe möglich sind — jeder Aufruf erhält den aktuellen vorherigen Wert.
useEffect ist ein Hook zur Durchführung von Nebeneffekten in funktionalen Komponenten. Er nimmt zwei Argumente entgegen: eine Effektfunktion und ein Abhängigkeitsarray. React führt den Effekt nach jedem Rendern aus, aber wenn Abhängigkeiten angegeben sind — nur wenn sich eine davon ändert.
Ein leeres Abhängigkeitsarray [] bedeutet, dass der Effekt einmal nach dem ersten Rendern ausgeführt wird (analog zu componentDidMount). Die Rückgabe einer Funktion aus dem Effekt ist eine Bereinigung, die beim Unmount (analog zu componentWillUnmount) und vor der erneuten Ausführung des Effekts ausgeführt wird.
import React, { useState, useEffect } from 'react';
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = new WebSocket(`wss://chat/${roomId}`);
socket.onmessage = (event) => {
setMessages(prev => [...prev, JSON.parse(event.data)]);
};
// Bereinigung - Socket schließen
return () => socket.close();
}, [roomId]);
return <ul>
{messages.map((msg, i) =>
<li key={i}>{msg.text}</li>
)}
</ul>;
}
Die Abhängigkeiten von useEffect sind die einzige Verbindung zwischen dem Effekt und der Außenwelt. Wenn der Effekt eine Variable aus Props oder State verwendet, muss sie im Abhängigkeitsarray stehen. Das Ignorieren dieser Regel ist eine Fehlerquelle mit veralteten Closures (stale closures).
useContext ist ein Hook zum Lesen von Kontextwerten. Er nimmt ein von React.createContext erstelltes Kontextobjekt entgegen und gibt den aktuellen Wert zurück. Wenn sich der Kontext ändert, wird die Komponente automatisch mit dem neuen Wert neu gerendert.
Vor useContext erforderte der Zugriff auf den Kontext in funktionalen Komponenten die Umwickelung in Context.Consumer, was Verschachtelung erzeugte. useContext eliminiert Consumer vollständig — rufen Sie den Hook einfach an einer beliebigen Stelle in der funktionalen Komponente auf.
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('hell');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button
className={`btn btn-${theme}`}
>
{theme === 'dark' ? 'moon' : '☀️'}
</button>;
}
useContext ist kein Ersatz für Redux oder globalen Zustand. React empfiehlt, Kontext für selten wechselnde Werte zu verwenden — Theme, Locale, Authentifizierungsstatus. Bei häufig aktualisierten Daten wird bei jedem erneuten Rendern die gesamten Kontextkonsumenten neu berechnet, was die Leistung beeinträchtigen kann.
useReducer ist eine Alternative zu useState für Zustände mit komplexer Aktualisierungslogik. Er nimmt eine Reducer-Funktion und einen Anfangszustand entgegen, gibt den aktuellen Zustand und eine Dispatch-Funktion zurück. Der Reducer erhält den aktuellen Zustand und eine Aktion, gibt einen neuen Zustand zurück — eine reine Funktion ohne Nebeneffekte.
useReducer ist besonders nützlich, wenn mehrere Aktionen (set, reset, increment, addItem) einen Zustand verwalten. Statt mehrerer useState mit verschiedenen Settern — ein Reducer mit klaren Aktionstypen. Dies macht die Logik vorhersagbar und leicht testbar.
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'setStep':
return { ...state, step: action.step };
case 'reset':
return { count: 0, step: 1 };
default:
throw new Error('Unknown action: ' + action.type);
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, {
count: 0,
step: 1
});
return (
<>
<p>Count: {state.count}</p>
<button
onClick={() => dispatch({ type: 'increment' })}>+</button>
<button
onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</>
);
}
useRef erzeugt ein veränderbares Objekt, das zwischen Renderings bestehen bleibt. Das Ändern von current löst kein erneutes Rendern aus. Hauptanwendungen sind der Zugriff auf DOM-Elemente und die Speicherung veränderbarer Werte (Timer, vorherige Werte, Bibliotheksinstanzen).
useMemo merkt sich das Ergebnis einer Funktion. Es berechnet den Wert nur neu, wenn sich eine Abhängigkeit ändert. Verwenden Sie es für teure Berechnungen — Filtern großer Arrays, Sortieren, Datenformatierung. useCallback ist ein Spezialfall von useMemo zum Merken von Funktionen.
import React, { useRef, useMemo, useCallback } from 'react';
function SearchPage({ items, query, onItemSelect }) {
const inputRef = useRef(null);
// Autofokus bei Montage
useEffect(() => {
inputRef.current?.focus();
}, []);
// Gefiltertes Ergebnis merken
const filtered = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase()
.includes(query.toLowerCase())
);
}, [items, query]);
// Callback merken
const handleSelect = useCallback((id) => {
onItemSelect(id);
}, [onItemSelect]);
return (
<div>
<input ref={inputRef}
value={query}
onChange={e => setQuery(e.target.value)}
/>
<ul>
{filtered.map(item =>
<li key={item.id}>
<button
onClick={() => handleSelect(item.id)}>
{item.name}
</button>
</li>
)}
</ul>
</div>
);
}
Eine wichtige Regel: useRef, useMemo und useCallback sind Optimierungswerkzeuge, keine zwingenden Konstrukte. Wickeln Sie nicht alles in useMemo — messen Sie zuerst die Leistung. Vorzeitige Optimierung verkompliziert den Code. React rendert Komponenten schnell; useMemo wird nur benötigt, wenn Berechnungen wirklich teuer sind (O(n²) und höher).
React führte zwei strenge Regeln für Hooks ein. Erstens: Rufen Sie Hooks nur auf der obersten Ebene auf — nicht innerhalb von Bedingungen, Schleifen oder verschachtelten Funktionen. Zweitens: Rufen Sie Hooks nur aus funktionalen React-Komponenten oder benutzerdefinierten Hooks auf. Das ESLint-Plugin eslint-plugin-react-hooks überprüft beide Regeln automatisch.
Der häufigste Fehler ist die Verletzung der Aufrufreihenfolge von Hooks. React verlässt sich auf die Reihenfolge der Hook-Aufrufe zwischen Renderings. Wenn ein Hook innerhalb einer Bedingung steht und bei einem Rendering nicht ausgeführt wird, verschieben sich alle nachfolgenden Hooks, was Fehler verursacht.
| Fehler | Was passiert | Lösung |
|---|---|---|
| Hook in Bedingung | Hook-Reihenfolge bricht | Bedingungslogik in den Hook verschieben |
| Stale closure | Effekt verwendet alten Wert | Alle Abhängigkeiten zum Array hinzufügen |
| Endlosschleife | useEffect ohne Abhängigkeiten ändert State | Abhängigkeiten angeben oder useReducer verwenden |
| Ref-Änderung während Render | Nebeneffekt im Funktionskörper | Mutationen in useEffect verschieben |
Der zweite häufige Fehler ist Stale Closure in useEffect. Wenn der Effekt eine Variable verwendet, die nicht in den Abhängigkeiten angegeben ist, „erfasst“ der Effekt ihren Wert zum Zeitpunkt der Closure-Erstellung. Die Lösung ist, alle verwendeten Variablen immer in das Abhängigkeitsarray aufzunehmen. Bei zu vielen Abhängigkeiten sollten Sie den Effekt in mehrere aufteilen.
Häufig gestellte Fragen
React verlässt sich auf die Reihenfolge der Hook-Aufrufe zwischen Renderings. Wenn ein Hook innerhalb einer Bedingung aufgerufen wird und bei einem Rendering nicht ausgelöst wird, verschieben sich alle nachfolgenden Hooks — ihr Zustand wird durcheinandergebracht. Die ESLint-Regel hooks/exhaustive-deps warnt davor.
useLayoutEffect ist synchron und wird ausgeführt, bevor der Browser Änderungen malt. useEffect ist asynchron — nach dem Malen. Verwenden Sie useLayoutEffect nur, wenn Sie DOM messen oder ändern müssen, bevor der Benutzer die Änderungen sieht (Popover-Position, Scroll-Wiederherstellung).
Verwenden Sie AbortController innerhalb von useEffect. Erstellen Sie einen Controller, übergeben Sie das Signal an fetch und rufen Sie controller.abort() in der Bereinigungsfunktion auf. Für ältere Browser verwenden Sie ein cancelled-Flag: return () => { cancelled = true; } mit einer Überprüfung vor setState.
Ein benutzerdefinierter Hook ist eine JavaScript-Funktion mit dem Präfix use, die eingebaute Hooks verwendet. Erstellen Sie einen, wenn dieselbe Logik mit Hooks in zwei oder mehr Komponenten wiederholt wird. Beispiele: useDebounce, useLocalStorage, useMediaQuery. Benutzerdefinierte Hooks ersetzen HOC und render props.
Im Strict Mode mountet und unmountet React die Komponente in der Entwicklung absichtlich zweimal, um Fehler mit ungereinigten Effekten zu finden. Wenn die Bereinigung korrekt geschrieben ist, verursacht der doppelte Aufruf keine Probleme. Im Produktionsmodus passiert dies nicht.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch