Wann React neu zeichnet
Warum dein Wert manchmal „alt“ ist und die Liste sich nicht rührt
React zeichnet nur neu, wenn du es über setState informierst – und beim Neuzeichnen bekommt deine Funktion einen festen Wert, der sich mitten im Klick nicht mehr ändert.
Drei Rätsel klären sich in dieser Lektion: „Warum steht da noch der alte Wert?“, „Warum passiert gar nichts?“ und „Warum ist plötzlich der Text in der falschen Zeile?“. Alle drei haben dieselbe Wurzel.
Der Wert ist für diesen Durchlauf eingefroren
Bei jedem RenderReact ruft deine Komponentenfunktion auf und schaut sich an, was sie zurückgibt. Mehr passiert dabei noch nicht.Das Ergebnis ist eine Beschreibung („so soll es aussehen“). Erst danach entscheidet React, was auf der Seite wirklich geändert wird.→ läuft deine Funktion komplett neu durch. count ist dabei keine Schublade, in die man hineinschaut – es ist eine Konstante für genau diesen Durchlauf.
function Zaehler() {
const [count, setCount] = useState(0); // in diesem Durchlauf: count = 0
function handleClick() {
setCount(count + 1); // heißt: 0 + 1 -> 1
setCount(count + 1); // heißt: immer noch 0 + 1 -> 1
console.log(count); // 0 – hier ändert sich nichts mehr
}
return <button onClick={handleClick}>{count}</button>;
}
// Nach einem Klick steht dort: 1. Nicht 2. Wie ein Foto: Der Klick arbeitet mit dem Bild, das beim letzten Zeichnen gemacht wurde. Du kannst dreimal draufzeigen – das Foto ändert sich davon nicht.
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
// Ergebnis: 1 setCount((c) => c + 1);
setCount((c) => c + 1);
setCount((c) => c + 1);
// Ergebnis: 3 Rechts übergibst du eine Funktion statt eines Werts. React ruft sie auf und schiebt jeweils den aktuellsten Stand hinein. Merksatz: Steht der alte Wert rechts vom Gleichheitszeichen, nimm die Funktionsform.
setCount(5) ändert keine Variable. Es sagt React: „zeichne bitte neu, diesmal mit 5“. Bis das passiert, gilt in deiner Funktion weiter der alte Wert. Das nennt sich BatchingReact sammelt mehrere Änderungen und zeichnet danach einmal neu, statt nach jeder einzelnen.→ – React sammelt alle Änderungen eines Klicks und zeichnet danach einmal neu.
Wann React überhaupt neu zeichnet
Es zeichnet neu, wenn …
• du setState mit einem NEUEN Wert aufrufst
• die Komponente darüber neu gezeichnet wird
• ein Context-Wert sich ändert
Es passiert NICHTS, wenn …
• du eine normale Variable änderst (let x = 1; x = 2)
• du ein Array/Objekt veränderst (liste.push(...))
• du ref.current setzt
• du setState mit demselben Wert aufrufst liste.push(x); setListe(liste) bewirkt nichts. React prüft nur: „ist das noch dasselbe Array wie vorher?“ – und ja, es ist dasselbe, du hast ja nur hineingelegt. Erzeuge stattdessen ein neues: setListe([...liste, x]).
▸ Genauer erklärt: warum reicht es nicht, den Inhalt zu ändern? optional
In JavaScript speichert eine Variable bei Objekten und Arrays nicht den Inhalt, sondern eine ReferenzBei Objekten und Arrays speichert JavaScript nicht den Inhalt, sondern die Adresse. Zwei Adressen sind verschieden, auch wenn der Inhalt gleich aussieht.Deshalb ist { a: 1 } === { a: 1 } in JavaScript false.→ – also die Adresse, wo der Inhalt liegt.
const a = [1, 2];
const b = a; // b zeigt auf DASSELBE Array
b.push(3);
a === b; // true – es ist ein und dasselbe Array
console.log(a); // [1, 2, 3]
const c = [...a]; // neue Kopie, neue Adresse
a === c; // false React vergleicht mit Object.isDie Vergleichsfunktion, mit der React prüft, ob sich ein Wert geändert hat. Sie arbeitet wie === (bei Objekten also über die Adresse).→, was ungefähr === entspricht. Es sieht also nur die Adresse. Gleiche Adresse heißt für React „nichts passiert“ – egal was du innen verändert hast.
Das ist kein Schikane, sondern Absicht: Adressen vergleichen dauert Nanosekunden, große Objekte Feld für Feld durchgehen dagegen lange. Deshalb die Regel unveränderlichNichts am Alten ändern, sondern immer eine geänderte Kopie erzeugen. In React die Regel für State.→, sondern immer eine geänderte Kopie erzeugen.
Listen: React braucht Namensschilder
Bei Listen kann React nicht an der Position erkennen, welcher Eintrag welcher ist. Dafür gibst du jedem ein keyEin Namensschild an Listeneinträgen, damit React erkennt, welcher Eintrag welcher ist – auch wenn sich die Reihenfolge ändert.→ – ein Namensschild aus den Daten.
{todos.map((todo) => <Zeile key={todo.id} todo={todo} />)} // ✅
{todos.map((todo, i) => <Zeile key={i} todo={todo} />)} // ⚠️ Vorsicht Löschst du den ersten Eintrag, rutschen alle Positionen eins hoch. React denkt: „Eintrag Nummer 0 hat neue Daten bekommen“ – und lässt alles stehen, was in dieser Zeile lebte: den getippten Text, den Haken in der Checkbox, den Cursor. Ergebnis: Der Text hängt plötzlich an der falschen Zeile.
Gibst du einer Komponente einen neuen key, wirft React sie weg und baut sie frisch auf – inklusive leerem State. <Formular key={userId} /> ist damit der sauberste Weg, ein Formular beim Nutzerwechsel zurückzusetzen.
Beim Zeichnen nichts anfassen
Im Funktionskörper deiner Komponente darf nichts stehen, was nach außen wirkt: kein Laden von Daten, kein document.title = …, keine Änderung an Props. Nur ausrechnen und zurückgeben. Solche Dinge nennt man SeiteneffektAlles, was über das reine Berechnen hinaus nach außen wirkt: etwas laden, einen Timer starten, den Seitentitel ändern.→ – sie gehören in Klick-Handler oder in useEffect.
Der StrictModeEin Prüfmodus, der während der Entwicklung absichtlich manches doppelt ausführt, um Fehler früh sichtbar zu machen.In der fertigen, veröffentlichten App passiert das nicht.→ ruft deine Komponenten absichtlich zweimal auf. Wenn dabei etwas kaputtgeht, hast du irgendwo einen Seiteneffekt versteckt. In der veröffentlichten App passiert das nicht – es ist reine Fehlersuche.
Sitzt das schon?
5 Fragen zu dieser Lektion. Falsche Antworten landen in deiner Statistik.
Jetzt selbst schreiben
Der Button fügt scheinbar nichts hinzu. Im Array kommt der Eintrag durchaus an – nur sieht man ihn nie. Finde den Grund und repariere ihn.
- □Nach einem Klick steht ein zusätzlicher Eintrag in der Liste
- □Nach drei Klicks sind es drei zusätzliche
- □Das vorhandene Array wird nicht verändert, sondern ersetzt
import { useState } from "react"; export default function App() { const [items, setItems] = useState<string[]>(["Erster Eintrag"]); function add() { // TODO: Warum sieht man die Änderung nicht? items.push("Eintrag " + (items.length + 1)); setItems(items); } return ( <div style={{ fontFamily: "system-ui", padding: 16 }}> <button onClick={add}>Hinzufügen</button> <ul> {items.map((item, i) => ( <li key={i}>{item}</li> ))} </ul> </div> ); }