Aufwärmen · Aufgabe 1 von 7
Zum Aufwärmen aus Lektion B1.2: Props. Welchen Text zeigt App?
function Greeting({ name, wins }) {
return <p>{name} has {wins + 1} wins</p>;
}
function App() {
return <Greeting name="Ada" wins={2} />;
}// B1.3 · ca. 30 Min. · Einstieg
Nach dieser Lektion reagieren Sie auf Klicks, halten Werte mit useState im State und lassen jedes Tic-Tac-Toe-Feld beim Klick ein X zeigen.
Danach können Sie
Aufwärmen · Aufgabe 1 von 7
function Greeting({ name, wins }) {
return <p>{name} has {wins + 1} wins</p>;
}
function App() {
return <Greeting name="Ada" wins={2} />;
}Vorhersagen · Aufgabe 2 von 7
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return <button onClick={handleClick}>Clicked {count} times</button>;
}Üben · Aufgabe 3 von 7
function Square() {
const [value, setValue] = useState(null);
function handleClick() {
setValue('X');
}
return <button className="square" onClick=____>{value}</button>;
}Üben · Aufgabe 4 von 7
Üben · Aufgabe 5 von 7
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return <button onClick={handleClick}>{count}</button>;
}
function App() {
return (
<>
<Counter />
<Counter />
</>
);
}Denksport · Aufgabe 6 von 7
function Counter() {
let count = 0;
function handleClick() {
count = count + 1;
}
return <button onClick={handleClick}>Clicked {count} times</button>;
}Anwenden · Aufgabe 7 von 7
Prüfen Sie Ihr Ergebnis anhand dieser Liste
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
Das ist das Brett aus dem Tutorial auf react.dev nach "Making an interactive component". Square bekommt kein Prop value mehr; es hält seinen Wert im State, beginnend mit null, jedes Feld ist also anfangs leer. handleClick ruft setValue('X') auf, und React rendert dieses Square neu, das jetzt X zeigt. Der Lauf unten zeigt das Brett vor jedem Klick. Im Browser bleibt die Seite aktiv: Klicken Sie ein Feld an, zeigt es X, die anderen acht bleiben leer.
main.jsx
import { useState } from 'react';
function Square() {
const [value, setValue] = useState(null);
function handleClick() {
setValue('X');
}
return (
<button className="square" onClick={handleClick}>
{value}
</button>
);
}
export default function Board() {
return (
<>
<div className="board-row">
<Square />
<Square />
<Square />
</div>
<div className="board-row">
<Square />
<Square />
<Square />
</div>
<div className="board-row">
<Square />
<Square />
<Square />
</div>
</>
);
}
styles.css
.board-row {
display: flex;
}
.square {
width: 2.5rem;
height: 2.5rem;
margin: 0 -1px -1px 0;
border: 1px solid #999;
background: #fff;
font-size: 1.5rem;
font-weight: bold;
}
Ausführen mit
npm run devAusgabe
<!-- #root -->
<div id="root"><div class="board-row"><button class="square"></button><button class="square"></button><button class="square"></button></div><div class="board-row"><button class="square"></button><button class="square"></button><button class="square"></button></div><div class="board-row"><button class="square"></button><button class="square"></button><button class="square"></button></div></div>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.
Übung 1 von 2
Lassen Sie jedes Feld sich einen Klick merken, wie im Tutorial. Deklarieren Sie in Square mit useState eine State-Variable value, beginnend mit null, zeigen Sie sie im Button und schreiben Sie ein handleClick, das sie auf 'X' setzt. Übergeben Sie handleClick an onClick des Buttons. Lassen Sie export vor Square stehen: Die Tests rendern es auch allein.
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.
Oben in Square: const [value, setValue] = useState(null);
Schreiben Sie in Square function handleClick() { setValue('X'); } und zeigen Sie {value} im Button.
Schreiben Sie onClick={handleClick}, ohne runde Klammern nach handleClick.
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
import { useState } from 'react';
export function Square() {
const [value, setValue] = useState(null);
function handleClick() {
setValue('X');
}
return (
<button className="square" onClick={handleClick}>
{value}
</button>
);
}
export default function Board() {
return (
<>
<div className="board-row">
<Square />
<Square />
<Square />
</div>
<div className="board-row">
<Square />
<Square />
<Square />
</div>
<div className="board-row">
<Square />
<Square />
<Square />
</div>
</>
);
}
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 function Square() {
// TODO: keep a value in state, starting at null, show it, and set it to 'X' on click.
return <button className="square"></button>;
}
export default function Board() {
return (
<>
<div className="board-row">
<Square />
<Square />
<Square />
</div>
<div className="board-row">
<Square />
<Square />
<Square />
</div>
<div className="board-row">
<Square />
<Square />
<Square />
</div>
</>
);
}
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 Board, {Square} from './main.jsx';
const labels = () => screen.getAllByRole('button').map((button) => button.textContent);
test('Alle neun Felder sind anfangs leer', () => {
render(<Board />);
assert.deepEqual(labels(), ['', '', '', '', '', '', '', '', ''], 'vor dem ersten Klick zeigen die Felder ' + JSON.stringify(labels()));
});
test('Ein Klick auf ein Feld zeigt X', async () => {
const user = userEvent.setup();
render(<Square />);
await user.click(screen.getByRole('button'));
const label = screen.getByRole('button').textContent;
assert.equal(label, 'X', 'nach einem Klick zeigt das Feld ' + JSON.stringify(label));
});
test('Jedes Feld hat seinen eigenen State', async () => {
const user = userEvent.setup();
render(<Board />);
await user.click(screen.getAllByRole('button')[4]);
assert.deepEqual(labels(), ['', '', '', '', 'X', '', '', '', ''], 'nach einem Klick auf das mittlere Feld zeigen die Felder ' + JSON.stringify(labels()));
});
test('React gibt keine Warnungen aus', async () => {
const warnings = [];
const original = console.error;
console.error = (...args) => warnings.push(args.join(' '));
try {
const user = userEvent.setup();
render(<Board />);
await user.click(screen.getAllByRole('button')[0]);
} finally {
console.error = original;
}
assert.deepEqual(warnings, [], 'console.error beim Rendern und Klicken');
});
styles.css
.board-row {
display: flex;
}
.square {
width: 2.5rem;
height: 2.5rem;
margin: 0 -1px -1px 0;
border: 1px solid #999;
background: #fff;
font-size: 1.5rem;
font-weight: bold;
}
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 devFü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
Das ist das Beispiel aus dem Quick Start auf react.dev mit einem Fehler: MyButton zählt in einer gewöhnlichen Variablen, die Buttons zeigen also immer Clicked 0 times. Halten Sie den Zähler stattdessen im State, sodass jeder Klick eins addiert und jeder Button für sich zählt.
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.
Führen Sie es aus und klicken Sie: Die Variable ändert sich, aber nichts wird neu gerendert.
Ersetzen Sie let count = 0 durch const [count, setCount] = useState(0);
Rufen Sie in handleClick setCount(count + 1) auf, statt count zu ändern.
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
import { useState } from 'react';
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return <button onClick={handleClick}>Clicked {count} times</button>;
}
export default function MyApp() {
return (
<div>
<h1>Counters that update separately</h1>
<MyButton />
<MyButton />
</div>
);
}
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';
function MyButton() {
// TODO: a plain variable does not update the screen; keep count in state.
let count = 0;
function handleClick() {
count = count + 1;
}
return <button onClick={handleClick}>Clicked {count} times</button>;
}
export default function MyApp() {
return (
<div>
<h1>Counters that update separately</h1>
<MyButton />
<MyButton />
</div>
);
}
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 MyApp from './main.jsx';
const labels = () => screen.getAllByRole('button').map((button) => button.textContent);
test('Beide Buttons beginnen mit Clicked 0 times', () => {
render(<MyApp />);
assert.deepEqual(labels(), ['Clicked 0 times', 'Clicked 0 times'], 'die Buttons zeigen ' + JSON.stringify(labels()));
});
test('Drei Klicks auf den ersten Button zeigen Clicked 3 times', async () => {
const user = userEvent.setup();
render(<MyApp />);
for (let i = 0; i < 3; i++) await user.click(screen.getAllByRole('button')[0]);
assert.equal(labels()[0], 'Clicked 3 times', 'nach drei Klicks zeigt der erste Button ' + JSON.stringify(labels()[0]));
});
test('Jeder Button zählt für sich', async () => {
const user = userEvent.setup();
render(<MyApp />);
const [first, second] = screen.getAllByRole('button');
await user.click(first);
await user.click(first);
await user.click(second);
assert.deepEqual(labels(), ['Clicked 2 times', 'Clicked 1 times'], 'nach zwei Klicks auf den ersten und einem auf den zweiten zeigen die Buttons ' + JSON.stringify(labels()));
});
test('React gibt keine Warnungen aus', async () => {
const warnings = [];
const original = console.error;
console.error = (...args) => warnings.push(args.join(' '));
try {
const user = userEvent.setup();
render(<MyApp />);
await user.click(screen.getAllByRole('button')[0]);
} finally {
console.error = original;
}
assert.deepEqual(warnings, [], 'console.error beim Rendern und Klicken');
});
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 devFü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.
import { useState } from 'react';
export default function Square() {
const [value, setValue] = useState(null);
function handleClick() {
setValue('X');
}
return <button onClick={handleClick()}>{value}</button>;
}
Was React ausgibt
Too many re-renders. React limits the number of renders to prevent an infinite loop.Warum, und die Lösung
Die runden Klammern rufen handleClick auf, während Square rendert. Es setzt State, das rendert Square neu, das ruft handleClick wieder auf, und React beendet die Schleife mit Uncaught Error. Übergeben Sie stattdessen die Funktion: onClick={handleClick}.
export default function Square() {
const [value, setValue] = useState(null);
function handleClick() {
setValue('X');
}
return <button onClick={handleClick}>{value}</button>;
}
Was React ausgibt
ReferenceError: useState is not definedWarum, und die Lösung
useState kommt aus dem Paket react, die Datei muss es also importieren: import { useState } from 'react'; ganz oben. Der Lauf bricht mit Uncaught ReferenceError ab, sobald Square rendert.
import { useState } from 'react';
const [value, setValue] = useState(null);
export default function Square() {
return <button>{value}</button>;
}
Was React ausgibt
Invalid hook call. Hooks can only be called inside of the body of a function component.Warum, und die Lösung
Hooks gehören oben in eine Komponente, nicht oben in die Datei. Hier läuft useState beim Laden der Datei, bevor React etwas rendert, deshalb warnt React, und der Lauf bricht mit TypeError: Cannot read properties of null (reading 'useState') ab. Verschieben Sie die Zeile in Square, vor alles andere.
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
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.