Die Regeln der Hooks sind zwei: Ruf Hooks nur auf der obersten Ebene deiner Komponente auf, nie in einer Bedingung, einer Schleife, einer verschachtelten Funktion oder nach einem frühen return; und ruf sie nur aus Funktionskomponenten oder aus Custom Hooks auf. Es gibt sie, weil React Hooks an der Reihenfolge ihrer Aufrufe unterscheidet, also muss diese Reihenfolge bei jedem Render gleich sein.
React speichert die Werte der Hooks einer Komponente in einer Liste, die an der Komponente hängt. Bei jedem Render geht es diese Liste durch: Der erste Hook-Aufruf bekommt den ersten Platz, der zweite Aufruf den zweiten und so weiter. Es gibt keine Namen, nur Positionen. Das Beispiel unten ist eine Spielzeugversion dieser Idee in ein paar Zeilen reinem JavaScript, nicht der Code von React selbst, also kannst du es gefahrlos kaputt machen.
Der erste Render füllt drei Plätze: Ada, Countess, 36. Klicke auf „Render without nickname“: Der bedingte Aufruf wird übersprungen, also nimmt der Aufruf für age Platz 1 und liest Countess. Das echte React bemerkt die Abweichung in manchen Fällen und wirft einen Fehler, in anderen gibt es den falschen Wert an den falschen Hook, was schlimmer ist. Verschiebe den Aufruf toyUseState('Countess') über das if, und beide Buttons liefern das richtige Alter.
Die zwei Regeln im Code
function Profile({ user }) {
// Good: always called, always in this order
const [tab, setTab] = useState('posts');
const theme = useContext(ThemeContext);
useEffect(() => {
document.title = user.name;
}, [user.name]);
// Bad: inside a condition, a loop or a nested function
if (user.isAdmin) {
const [log, setLog] = useState([]); // breaks the order
}
}
Ein Hook-Aufruf, der immer erreicht wird, in derselben Reihenfolge, ist überall im Körper in Ordnung, solange er vor jedem frühen return steht.
Einen bedingt aufgerufenen Hook beheben
Der Linter meldet das als React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render. Die Lösung ist immer dieselbe: Ruf den Hook jedes Mal auf und setze die Bedingung hinein.
// Broken: the effect exists on some renders and not others
function Notifications({ user }) {
if (user) {
useEffect(() => {
console.log('subscribe for', user.name);
}, [user]);
}
return null;
}
Der Effekt wird bei jedem Render deklariert, und das if darin entscheidet, ob er etwas tut. Melde dich ein paar Mal an und ab, und die Konsole zeigt ein Abonnieren für jede Anmeldung und ein Abbestellen für jede Abmeldung.
Hooks nach einem frühen return
Ein frühes return ist auch eine Bedingung. Das erzeugt „Rendered more hooks than during the previous render“, sobald loading zum ersten Mal false wird, weil dieser Render einen Hook erreicht, den der vorherige nie aufgerufen hat. Wenn ein Render weniger Hooks aufruft als der vorherige, lautet die Meldung „Rendered fewer hooks than expected“.
// Broken: useState is skipped while loading
function Comments({ loading, comments }) {
if (loading) return <p>Loading...</p>;
const [expanded, setExpanded] = useState(false);
return <List items={comments} expanded={expanded} />;
}
Jeder Hook steht über dem return, also rufen beide Zweige dieselben Hooks auf. Klicke auf „Toggle loading“, klapp die Liste auf und schalte das Laden dann zweimal um: Die Liste weiß noch, dass sie aufgeklappt war, weil der State nie übersprungen wurde.
Hooks in einer Schleife
Du kannst useState nicht einmal pro Eintrag in einer Schleife aufrufen, weil sich die Anzahl der Einträge ändert. Gib jedem Eintrag eine eigene Komponente und ruf den Hook dort auf; jede Instanz hat ihre eigenen Plätze.
„Invalid hook call“
Die Meldung beginnt mit „Invalid hook call. Hooks can only be called inside of the body of a function component.“ Sie hat drei übliche Ursachen:
- Der Hook läuft, während React keine Funktionskomponente rendert. In einem Event-Handler, in einem Effekt-Callback, in einer Klassenkomponente oder auf Modulebene. Verschiebe ihn in den Komponentenkörper oder einen Custom Hook und ruf den Custom Hook aus der Komponente auf.
- Die Versionen von
reactundreact-dompassen nicht zusammen. Installiere von beiden dieselbe Version. - Es gibt zwei Kopien von React. Ein verlinktes lokales Paket oder eine Bibliothek, die ihr eigenes React mitbringt, fügt eine zweite Kopie hinzu, und Hooks aus der einen Kopie funktionieren nicht mit dem Renderer der anderen. Führe
npm ls reactaus und sorg dafür, dass nur eine Version erscheint.
npm ls react
Die Vorschau hier läuft wie ein Produktions-Build, der diese Meldungen nicht ausgibt: Ein aus einem Event-Handler aufgerufener Hook scheitert mit einem einfachen TypeError wie „Cannot read properties of null“, und Reacts eigene Fehler kommen als kurze Codes „Minified React error“ an. Führe deine App im Entwicklungsmodus aus, um den vollständigen Text zu lesen.
Lass den Linter es durchsetzen
eslint-plugin-react-hooks prüft beide Regeln beim Tippen und außerdem die Abhängigkeitsarrays von Effekten. Das React-Template von Vite und die ESLint-Konfiguration von Next.js enthalten es. In einer Flat Config von ESLint schaltest du sein empfohlenes Preset ein:
// eslint.config.js
import reactHooks from 'eslint-plugin-react-hooks';
import { defineConfig } from 'eslint/config';
export default defineConfig([reactHooks.configs.flat.recommended]);
Die zwei Kernregeln sind react-hooks/rules-of-hooks (ein Fehler) und react-hooks/exhaustive-deps (eine Warnung). Seit Version 7 schaltet das Preset außerdem Regeln aus dem React Compiler ein, etwa react-hooks/purity und react-hooks/refs, die unpure Renders und das Lesen von Refs beim Rendern markieren. Um Regeln einzeln auszuwählen, registriere das Plugin als plugins: { 'react-hooks': reactHooks } und führe die gewünschten Regeln auf.
Das Plugin findet Hooks am Namen: Jede Funktion, deren Name mit use gefolgt von einem Großbuchstaben beginnt, gilt als Hook, und nur Komponenten (großgeschriebene Namen) und andere use-Funktionen dürfen sie aufrufen. Deshalb müssen Custom Hooks das Präfix nutzen.
Die Ausnahme: use
use, in React 19 hinzugekommen, liest ein Promise oder einen Context und darf anders als jeder andere Hook in einer Bedingung oder Schleife aufgerufen werden. Es muss trotzdem beim Rendern aufgerufen werden, in einer Komponente oder einem Hook.
Nur die Überschrift mit Theme ändert ihre Farbe, wenn du das Theme wechselst. Probiere dasselbe mit useContext im if, und der Linter markiert es. Die Seite zum use-Hook behandelt das Lesen von Promises damit.
Häufig gestellte Fragen
Was sind die Regeln der Hooks?
Zwei Regeln. Ruf Hooks nur auf der obersten Ebene einer Funktionskomponente oder eines Custom Hooks auf, nie in Bedingungen, Schleifen, verschachtelten Funktionen oder nach einem frühen return. Und ruf Hooks nur aus Funktionskomponenten oder Custom Hooks auf, nicht aus normalen Funktionen oder Klassenkomponenten.
Warum können Hooks nicht bedingt aufgerufen werden?
React kennt deine Hooks nicht beim Namen. Es speichert ihre Werte in einer Liste und gibt sie in der Reihenfolge der Aufrufe zurück. Wenn eine Bedingung einen Aufruf überspringt, liest jeder spätere Hook den Wert, der zum Hook davor gehörte.
Was bedeutet „Rendered more hooks than during the previous render“?
Ein Render hat mehr Hooks aufgerufen als der Render davor, meist weil ein Hook in einem if oder nach einem frühen return steht, das diesmal anders gelaufen ist. Verschiebe jeden Hook über die Bedingung und setze die Bedingung stattdessen in den Hook.
Wie behebe ich „Invalid hook call“?
Prüfe drei Dinge: Der Hook läuft, während React eine Funktionskomponente rendert (nicht in einem Event-Handler, einem Effekt-Callback, einer Klassenkomponente oder auf Modulebene), react und react-dom haben passende Versionen, und die App hat nur eine Kopie von React (npm ls react listet sie).
Darf der use-Hook bedingt aufgerufen werden?
Ja. use (React 19) ist die einzige Ausnahme: Du kannst ihn in if-Blöcken und Schleifen aufrufen. Er muss trotzdem in einer Komponente oder einem Hook aufgerufen werden, nicht in einem Event-Handler.