Nach dieser Lektion richten Sie Ihren Editor für React ein, lassen einen Linter die Rules of Hooks prüfen und Prettier beim Speichern formatieren, und reparieren Komponenten, die diese Regeln brechen.
Die Editoren nennen, die react.dev aufführt, und Prettier mit Formatieren beim Speichern einrichten
Erklären, wozu ein Linter dient und welche Hook-Regeln ESLint und oxlint aus der Vite-Vorlage prüfen
Komponenten finden und reparieren, die die Rules of Hooks brechen
Aufwärmen · Aufgabe 1 von 7
Zum Aufwärmen aus Lektion B4.2: Die src/main.jsx der Vorlage legt <StrictMode> um App, und npm run dev liefert sie aus. Die Seite lädt, und Sie klicken einmal auf den Button. Wie oft zeigt die Konsole render?
function Counter() {
console.log('render');
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>count is {count}</button>;
}
Vorhersagen · Aufgabe 2 von 7
Sagen Sie voraus, bevor Sie weiterlesen. Ticket kehrt früh zurück, sobald es benutzt ist, und ruft nach diesem return ein zweites useState auf. Was passiert, wenn Sie auf den Button klicken? (Die Antworten sind die Ausgaben des Programms.)
Ordnen Sie jeder Stelle, an der ein Hook aufgerufen wird, die Reparatur zu, die react.dev auf der Seite Rules of Hooks nennt.
Üben · Aufgabe 4 von 7
Ergänzen Sie den Befehl, den die package.json der React-Vorlage von Vite für npm run lint ausführt (create-vite 9.2.1).
"lint": "",
Üben · Aufgabe 5 von 7
Votes ruft useState in map auf, über ein festes Array mit drei Einträgen. Das bricht die Rules of Hooks. Was passiert beim Einhängen? (Die Antworten sind die Ausgaben des Programms.)
Knobelaufgabe. Badge ruft ein zweites useState nur für Admins auf. Page startet mit isAdmin true, und ihr Button schaltet es um. Wann wirft React? (Die Antworten sind die Ausgaben des Programms.)
Mini-Aufgabe: Richten Sie Ihren Editor um das Projekt aus Lektion B4.2 ein. Führen Sie in my-app npm run lint aus. Fügen Sie dann in src/App.jsx ein zweites useState in einem if ein und führen Sie npm run lint erneut aus: Lesen Sie die Regel und die Meldung. Reparieren Sie es, indem Sie den Hook auf die oberste Ebene ziehen, und führen Sie npm run lint aus, bis es die Regel nicht mehr meldet. Installieren Sie in VS Code die Prettier-Erweiterung und schalten Sie format on save ein; verderben Sie die Einrückung von App.jsx und speichern Sie.
Prüfen Sie Ihr Ergebnis anhand dieser Liste
Selbst programmieren
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
Hooks oben, Verzweigungen darunter
TeaCard ruft zuerst ihre Hooks auf, bei jedem Rendern, in derselben Reihenfolge: useToggle, einen kleinen eigenen Hook, dessen Name mit use beginnt, und useState für die Menge. Erst dann verzweigt sie, im JSX, mit open &&. Die Timer am Ende handeln wie ein Nutzer: Sie klicken Show details, dann Add one. Der Runner wartet auf sie und gibt dann #root aus.
main.jsx
import { useState } from 'react';
// A custom Hook: its name starts with use, and it calls a Hook.
function useToggle(initialValue) {
const [on, setOn] = useState(initialValue);
return [on, () => setOn(!on)];
}
export default function TeaCard() {
// Every Hook at the top level, in the same order on every render.
const [open, toggle] = useToggle(false);
const [quantity, setQuantity] = useState(1);
// Branch below the Hooks: here, inside the JSX.
return (
<section>
<h2>Green tea</h2>
<button onClick={toggle}>{open ? 'Hide details' : 'Show details'}</button>
{open && (
<p>
Quantity: {quantity}{' '}
<button onClick={() => setQuantity(quantity + 1)}>Add one</button>
</p>
)}
</section>
);
}
// A run only mounts. Act like a user: open the details, then add one.
setTimeout(() => {
document.querySelector('button').click();
setTimeout(() => {
document.querySelectorAll('button')[1].click();
}, 50);
}, 50);
quantity gibt es ab dem ersten Rendern, solange die Details noch zu sind, also ändert sich die Reihenfolge der Hooks nie.
useToggle ist ein eigener Hook: eine Funktion, deren Name mit use beginnt und die andere Hooks aufruft. Für ihn gelten dieselben Regeln.
Im Browser bleibt die Seite aktiv: Klicken Sie Hide details und Show details, und die Menge bleibt erhalten.
Jeder Hook hier wird auf der obersten Ebene einer Komponente oder eines eigenen Hooks aufgerufen, wie es die Rules of Hooks verlangen.
Ändern und ausführen
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser React und den Compiler herunter, der JSX in JavaScript übersetzt (bis zu 2 MB), und speichert beides im Cache. Ihre Komponenten laufen in Ihrem Browser und bleiben auf Ihrem Gerät.
Ihre Seite
Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.
Führen Sie das Programm aus, um seine Seite hier zu sehen.
Übungen
Übung 1 von 2
Ein Hook in einem if
TeaCard ruft useState für die Menge erst auf, wenn die Karte offen ist, also macht Show details die Seite kaputt. Ziehen Sie diesen Hook auf die oberste Ebene, damit beide Hooks bei jedem Rendern laufen, und lassen Sie den Rest, wie er ist. Die Tests öffnen die Karte, fügen zwei hinzu, verbergen sie und zeigen sie wieder: Die Menge muss 3 sein.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser React und den Compiler herunter, der JSX in JavaScript übersetzt (bis zu 2 MB), und speichert beides im Cache. Ihre Komponenten laufen in Ihrem Browser und bleiben auf Ihrem Gerät.
Ihre Seite
Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.
Führen Sie das Programm aus, um seine Seite hier zu sehen.
Hinweise
Hinweis 1
Klicken Sie unter Ausführen auf Show details: React wirft, weil das zweite Rendern einen Hook mehr aufruft als das erste.
Hinweis 2
Ein Hook darf vor einem frühen return stehen, nie danach oder im if.
Hinweis 3
Ziehen Sie const [quantity, setQuantity] = useState(1); direkt unter das erste useState.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
Installieren Sie React 19.3 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.jsx
import { useState } from 'react';
export default function TeaCard() {
const [open, setOpen] = useState(false);
if (open) {
// TODO: this Hook is only called once the card is open.
const [quantity, setQuantity] = useState(1);
return (
<section>
<button onClick={() => setOpen(false)}>Hide details</button>
<p>Quantity: {quantity}</p>
<button onClick={() => setQuantity(quantity + 1)}>Add one</button>
</section>
);
}
return (
<section>
<button onClick={() => setOpen(true)}>Show details</button>
</section>
);
}
main.test.jsx
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {render, screen} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import TeaCard from './main.jsx';
test('Show details öffnet die Karte mit der Menge 1', async () => {
const user = userEvent.setup();
render(<TeaCard />);
await user.click(screen.getByRole('button', {name: 'Show details'}));
const quantity = screen.queryByText(/^Quantity:/);
assert.equal(quantity?.textContent, 'Quantity: 1', 'nach Show details zeigt die Seite ' + JSON.stringify(document.body.textContent));
});
test('Add one zählt, und der Wert übersteht Hide und Show', async () => {
const user = userEvent.setup();
render(<TeaCard />);
await user.click(screen.getByRole('button', {name: 'Show details'}));
const add = screen.queryByRole('button', {name: 'Add one'});
assert.ok(add !== null, 'nach Show details gibt es keinen Button "Add one"');
await user.click(add);
await user.click(screen.getByRole('button', {name: 'Add one'}));
await user.click(screen.getByRole('button', {name: 'Hide details'}));
await user.click(screen.getByRole('button', {name: 'Show details'}));
const quantity = screen.queryByText(/^Quantity:/);
assert.equal(quantity?.textContent, 'Quantity: 3', 'nach zweimal Add one, Hide und Show zeigt die Seite ' + JSON.stringify(document.body.textContent));
});
Legen Sie main.jsx in einem Vite-React-Projekt (npm create vite@latest, Vorlage react) als App.jsx in src/ ab und starten Sie es mit npm run dev. React rendert es dort wie hier in <StrictMode>.
Programm ausführen:
npm run dev
Für die Prüfungen gibt es noch keinen Befehl auf Ihrem Computer. Sie stehen in main.test.jsx, geschrieben mit node:test und React Testing Library.
Übung 2 von 2
Hooks in einer Schleife
Poll ruft useState in map auf, eines pro Option. Das funktioniert, bis Add Juice eine Option hinzufügt und damit einen Hook mehr. Geben Sie jeder Option eine eigene Komponente, Option, mit eigenem useState, und rendern Sie eine Option pro Eintrag, mit key. Die Stimmen müssen pro Option zählen und Add Juice überstehen.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser React und den Compiler herunter, der JSX in JavaScript übersetzt (bis zu 2 MB), und speichert beides im Cache. Ihre Komponenten laufen in Ihrem Browser und bleiben auf Ihrem Gerät.
Ihre Seite
Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.
Führen Sie das Programm aus, um seine Seite hier zu sehen.
Hinweise
Hinweis 1
Klicken Sie unter Ausführen auf Add Juice: React wirft, weil das Rendern useState einmal öfter aufruft.
Hinweis 2
Eine Komponente ruft ihre Hooks auf ihrer eigenen obersten Ebene auf, also kann jede Option ein eigenes useState haben.
Hinweis 3
Schreiben Sie function Option({ name }) mit const [count, setCount] = useState(0); und rendern Sie in map <Option key={option} name={option} />.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
Installieren Sie React 19.3 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {render, screen} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Poll from './main.jsx';
test('Jede Option zählt ihre eigenen Stimmen', async () => {
const user = userEvent.setup();
render(<Poll />);
await user.click(screen.getByRole('button', {name: 'Vote for Tea'}));
await user.click(screen.getByRole('button', {name: 'Vote for Tea'}));
await user.click(screen.getByRole('button', {name: 'Vote for Coffee'}));
assert.equal(screen.queryByText(/^Tea:/)?.textContent, 'Tea: 2', 'Tea nach zwei Stimmen');
assert.equal(screen.queryByText(/^Coffee:/)?.textContent, 'Coffee: 1', 'Coffee nach einer Stimme');
});
test('Add Juice behält die Stimmen, und Juice zählt auch', async () => {
const user = userEvent.setup();
render(<Poll />);
await user.click(screen.getByRole('button', {name: 'Vote for Tea'}));
await user.click(screen.getByRole('button', {name: 'Add Juice'}));
const juice = screen.queryByRole('button', {name: 'Vote for Juice'});
assert.ok(juice !== null, 'nach Add Juice zeigt die Seite ' + JSON.stringify(document.body.textContent));
await user.click(juice);
assert.equal(screen.queryByText(/^Tea:/)?.textContent, 'Tea: 1', 'Tea nach einer Stimme und Add Juice');
assert.equal(screen.queryByText(/^Juice:/)?.textContent, 'Juice: 1', 'Juice nach einer Stimme');
});
Legen Sie main.jsx in einem Vite-React-Projekt (npm create vite@latest, Vorlage react) als App.jsx in src/ ab und starten Sie es mit npm run dev. React rendert es dort wie hier in <StrictMode>.
Programm ausführen:
npm run dev
Für die Prüfungen gibt es noch keinen Befehl auf Ihrem Computer. Sie stehen in main.test.jsx, geschrieben mit node:test und React Testing Library.
Häufige Fehler
Ein Hook nach einem frühen return
import { useState } from 'react';
function Order({ sent }) {
const [items] = useState(2);
if (sent) {
return <p>Order sent.</p>;
}
const [note] = useState('');
return <p>{items} items, note: {note || 'none'}</p>;
}
export default function App() {
const [sent, setSent] = useState(false);
return (
<>
<Order sent={sent} />
<button onClick={() => setSent(true)}>Send</button>
</>
);
}
// A run only mounts. Act like a user: click Send.
setTimeout(() => document.querySelector('button').click(), 50);
Was React ausgibt
Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
Warum, und die Lösung
Das erste Rendern ruft in Order zwei Hooks auf; nach Send überspringt das frühe return den zweiten. Ziehen Sie const [note] = useState(''); über das if, damit jedes Rendern beide aufruft. react.dev: Rufen Sie Hooks immer auf der obersten Ebene auf, vor jedem frühen return.
Ein Effect nur unter einer Bedingung
import { useEffect, useRef, useState } from 'react';
export default function Search() {
const [query, setQuery] = useState('');
if (query !== '') {
useEffect(() => {
document.title = 'Search: ' + query;
});
}
const inputRef = useRef(null);
return (
<>
<input ref={inputRef} value={query} readOnly />
<button onClick={() => setQuery('tea')}>Search tea</button>
</>
);
}
// A run only mounts. Act like a user: click Search tea.
setTimeout(() => document.querySelector('button').click(), 50);
Was React ausgibt
React has detected a change in the order of Hooks called by Search.
Warum, und die Lösung
Nach dem Klick ist der zweite Hook, den Search aufruft, useEffect statt useRef, also warnt React, gibt eine Tabelle beider Reihenfolgen aus und wirft dann. Rufen Sie useEffect bei jedem Rendern auf und setzen Sie die Bedingung hinein: useEffect(() => { if (query !== '') document.title = 'Search: ' + query; });. react.dev: Rufen Sie Hooks nicht in Bedingungen oder Schleifen auf.
Ein Hook in useMemo
import { useMemo, useState } from 'react';
export default function Price() {
// useMemo, a Hook you meet later, caches a calculation.
const label = useMemo(() => {
const [cents] = useState(250);
return '€' + (cents / 100).toFixed(2);
}, []);
return <p>Green tea {label}</p>;
}
Was React ausgibt
Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks.
Warum, und die Lösung
Die an useMemo übergebene Funktion ist nicht die oberste Ebene einer Komponente, auch wenn sie läuft, während Price rendert. React warnt schon beim ersten Rendern. Rufen Sie useState auf der obersten Ebene von Price auf und verwenden Sie nur den Wert im Callback: const [cents] = useState(250); const label = useMemo(() => '€' + (cents / 100).toFixed(2), [cents]);.
React im Browser: React 19.3.0 (Entwicklungs-Build) und React Testing Library, MIT, von dieser Website ausgeliefert; JSX übersetzt TypeScript 6.0.3, Apache-2.0. Lizenz und Quellcode
Abschlussquiz
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.
Kernideen
Ein Editor und ein Linter
react.dev nennt VS Code einen der beliebtesten Editoren und führt außerdem WebStorm, Sublime Text und Vim auf. Ein Linter findet Probleme in Ihrem Code, während Sie schreiben. react.dev empfiehlt ESLint und rät, alle Regeln von eslint-plugin-react-hooks einzuschalten: Sie fangen die schwersten Fehler früh ab. Die React-Vorlage von Vite bringt einen anderen Linter mit, oxlint: In einem Lauf am 05.10.2026 stand in package.json "lint": "oxlint", und .oxlintrc.json setzte react/rules-of-hooks auf error. Für ein useState in einem if gab oxlint 1.86.0 aus: React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render. Eine Editor-Erweiterung zeigt solche Probleme schon beim Tippen.
Prettier und Formatieren beim Speichern
Prettier formatiert Ihren Code nach festen, einstellbaren Regeln um: Einrückung, Anführungszeichen, Tabs oder Leerzeichen. Niemand muss darüber streiten. Öffnen Sie in VS Code Quick Open (Strg/Cmd+P), fügen Sie ext install esbenp.prettier-vscode ein und drücken Sie Enter. Drücken Sie dann Strg/Cmd+Umschalt+P, tippen Sie settings, suchen Sie nach format on save und haken Sie es an: Laut react.dev formatieren Sie idealerweise bei jedem Speichern. Hat Ihr ESLint-Preset Formatierungsregeln, können sie mit Prettier kollidieren; react.dev empfiehlt eslint-config-prettier, um sie abzuschalten, damit ESLint nur logische Fehler findet. Damit nur formatierte Dateien in einen Pull Request gelangen, führen Sie prettier --check in der Continuous Integration aus.
Die Rules of Hooks
Funktionen, deren Name mit use beginnt, sind Hooks, etwa useState oder Ihr eigenes useToggle. Rufen Sie sie nur auf der obersten Ebene einer Komponente oder eines eigenen Hooks auf, vor jedem frühen return: nicht in Bedingungen, Schleifen, Event-Handlern, an useMemo oder useEffect übergebenen Funktionen oder try/catch. Hooks verlassen sich auf eine feste Aufrufreihenfolge bei jedem Rendern. Manche Verstöße bemerkt React zur Laufzeit: Ein Hook mehr nach einem Klick wirft "Rendered more hooks than during the previous render.", und ein Hook in useMemo erzeugt eine Warnung. Ein Hook in einer Schleife über ein festes Array läuft still: Nur der Linter sieht ihn. Zur Reparatur ziehen Sie den Hook nach oben und verzweigen darunter, oder Sie geben jedem Eintrag eine eigene Komponente.