React wird einfacher, wenn du die richtigen Mentalmodelle verstehst: Komponenten, Rendering, State und Effects. Lerne die Kernideen und baue schnell vorhersehbare UIs – auch per Chat-Generator.

React kann anfangs frustrierend sein, weil du die UI eine Änderung sehen kannst, aber nicht immer erklären kannst, warum sie passiert ist. Du klickst einen Button, etwas aktualisiert sich, und dann überrascht dich ein anderer Bereich der Seite. Das ist meist nicht „React ist seltsam.“ Sondern: „Mein Bild davon, was React tut, ist unscharf.“
Ein Mentalmodell ist die einfache Geschichte, die du dir darüber erzählst, wie etwas funktioniert. Wenn die Geschichte falsch ist, triffst du selbstsichere Entscheidungen, die zu verwirrenden Ergebnissen führen. Denk an ein Thermostat: ein schlechtes Modell ist „Ich stelle 22°C ein, also wird es sofort 22°C im Raum.“ Ein besseres Modell ist „Ich stelle ein Ziel ein, und der Heizkörper schaltet über die Zeit an und aus, um es zu erreichen.“ Mit der besseren Geschichte wirkt das Verhalten nicht mehr zufällig.
React funktioniert genauso. Sobald du ein paar klare Ideen übernommen hast, wird React vorhersehbar: Du kannst dir die aktuellen Daten anschauen und zuverlässig erraten, was auf dem Bildschirm stehen wird.
Dan Abramov hat dieses „mach es vorhersehbar“-Mindset populär gemacht. Es geht nicht darum, Regeln auswendig zu lernen. Es geht darum, ein kleines Set an Wahrheiten im Kopf zu behalten, sodass du durch Nachdenken und nicht durch Versuch und Irrtum debuggen kannst.
Behalte diese Ideen im Blick:
Hältst du dich daran, hört React auf, wie Magie zu wirken. Es wird zu einem System, dem du vertrauen kannst.
React wird leichter, wenn du aufhörst, in „Screens“ zu denken, und anfängst, in kleinen Teilen zu denken. Eine Komponente ist eine wiederverwendbare UI-Einheit. Sie nimmt Eingaben und gibt eine Beschreibung dessen zurück, wie die UI für diese Eingaben aussehen sollte.
Es hilft, eine Komponente als reine Beschreibung zu behandeln: „gegeben diese Daten, zeige das.“ Diese Beschreibung kann an vielen Stellen verwendet werden, weil sie nicht davon abhängt, wo sie lebt.
Props sind die Eingaben. Sie kommen von einer Elternkomponente. Props werden nicht „von der Komponente besessen“ und sollten nicht stillschweigend von der Komponente verändert werden. Wenn ein Button label="Save" erhält, ist die Aufgabe des Buttons, dieses Label zu rendern, nicht zu entscheiden, dass es anders sein sollte.
State ist besessene Daten. Es ist das, woran sich die Komponente über die Zeit erinnert. State ändert sich, wenn ein Nutzer interagiert, wenn eine Anfrage fertig ist oder wenn du entscheidest, dass etwas anders sein sollte. Im Gegensatz zu Props gehört State dieser Komponente (oder der Komponente, die du als Besitzer ausgewählt hast).
Die kurze Version der Kernidee: UI ist eine Funktion des State. Wenn der State „loading“ sagt, zeige einen Spinner. Wenn State „error“ sagt, zeige eine Nachricht. Wenn State „items = 3“ sagt, rendere drei Zeilen. Deine Aufgabe ist, die UI aus dem State lesen zu lassen, nicht in versteckte Variablen abdriften zu lassen.
Eine schnelle Trennung der Konzepte:
SearchBox, ProfileCard, CheckoutForm)name, price, disabled)isOpen, query, selectedId)Beispiel: ein Modal. Der Parent kann title und onClose als Props übergeben. Das Modal könnte isAnimating im State besitzen.
Selbst wenn du UI per Chat generierst (zum Beispiel auf Koder.ai), bleibt diese Trennung der schnellste Weg, um den Überblick zu behalten: Entscheide zuerst, was Props vs. State ist, dann lass die UI folgen.
Eine nützliche Art, React im Kopf zu behalten (sehr im Sinne von Dan Abramov) ist: Rendering ist eine Berechnung, kein Malvorgang. React führt deine Komponentenfunktionen aus, um herauszufinden, wie die UI für die aktuellen Props und den aktuellen State aussehen sollte. Die Ausgabe ist eine UI-Beschreibung, keine Pixel.
Ein Re-Render bedeutet einfach, dass React diese Berechnung wiederholt. Es heißt nicht „die ganze Seite wird neu gezeichnet.“ React vergleicht das neue Ergebnis mit dem vorherigen und wendet die kleinste Menge an Änderungen am echten DOM an. Viele Komponenten können re-rendern, während nur wenige DOM-Knoten tatsächlich aktualisiert werden.
Die meisten Re-Renders passieren aus einigen einfachen Gründen: der State einer Komponente hat sich geändert, ihre Props haben sich geändert, oder ein Parent hat re-rendered und React bat das Kind erneut zu rendern. Letzteres überrascht oft Leute, ist aber meist unproblematisch. Wenn du Render als „billig und langweilig“ behandelst, bleibt deine App leichter nachvollziehbar.
Die Faustregel, die das sauber hält: mache Render rein. Gegeben die gleichen Eingaben (Props + State) sollte deine Komponente die gleiche UI-Beschreibung zurückgeben. Halte Überraschungen aus dem Render heraus.
Konkretes Beispiel: wenn du eine ID mit Math.random() im Render generierst, ändert sie sich bei einem Re-Render und plötzlich verliert ein Checkbox-Fokus oder ein Listeneintrag remounted. Erzeuge die ID einmal (State, memo oder außerhalb der Komponente) und das Rendern bleibt stabil.
Merke dir einen Satz: Ein Re-Render bedeutet „berechne neu, wie die UI sein sollte“, nicht „baue alles neu auf“.
Ein weiteres hilfreiches Modell: State-Updates sind Anfragen, keine sofortigen Zuweisungen. Wenn du einen Setter wie setCount(count + 1) aufrufst, bittest du React, ein Render mit einem neuen Wert einzuplanen. Wenn du State direkt danach liest, siehst du möglicherweise noch den alten Wert, weil React noch nicht gerendert hat.
Darum sind „kleine und vorhersehbare“ Updates wichtig. Beschreibe die Änderung, anstatt dich auf den vermeintlich aktuellen Wert zu verlassen. Wenn der nächste Wert vom vorherigen abhängt, benutze die Updater-Form: setCount(c => c + 1). Das passt zu Reacts Arbeitsweise: mehrere Updates können in eine Warteschlange gestellt und dann in Reihenfolge angewendet werden.
Unveränderlichkeit ist die andere Hälfte des Bildes. Verändere Objekte und Arrays nicht in-place. Erzeuge ein neues Objekt/Array mit der Änderung. React kann dann erkennen: „dieser Wert ist neu“ und dein Gehirn kann nachverfolgen, was sich geändert hat.
Beispiel: Ein Todo-Item togglen. Der sichere Ansatz ist, ein neues Array und ein neues Todo-Objekt für das geänderte Item zu erstellen. Der riskante Ansatz ist, todo.done = !todo.done im bestehenden Array zu drehen.
Halte State außerdem minimal. Eine häufige Falle ist das Speichern von Werten, die du berechnen kannst. Wenn du bereits items und filter hast, speichere nicht filteredItems im State. Berechne es während des Renderns. Weniger State-Variablen bedeuten weniger Möglichkeiten, dass Werte auseinanderlaufen.
Ein einfacher Test, was in den State gehört:
Wenn du UI per Chat baust (auch auf Koder.ai), bitte um Änderungen als kleine Patches: „Füge ein boolean-Flag hinzu“ oder „Aktualisiere diese Liste unveränderlich.“ Kleine, explizite Änderungen halten den Generator und deinen React-Code im Einklang.
Rendering beschreibt die UI. Effects synchronisieren mit der Außenwelt. „Außen“ sind Dinge, die React nicht kontrolliert: Netzwerkanfragen, Timer, Browser-APIs und manchmal imperatives DOM-Arbeiten.
Wenn etwas aus Props und State berechnet werden kann, sollte es normalerweise nicht in einem Effect leben. Es in einen Effect zu setzen fügt einen zweiten Schritt hinzu (rendern, Effect ausführen, State setzen, erneut rendern). Dieser zusätzliche Hop ist der Ort, an dem Flackern, Loops und „warum ist das veraltet?“-Bugs auftreten.
Eine häufige Verwirrung: Du hast firstName und lastName und speicherst fullName im State mit einem Effect. fullName ist aber keine Nebenwirkung. Berechne es während des Renderns und es stimmt immer.
Als Gewohnheit: leite UI-Werte während des Renderns ab (oder mit useMemo, wenn etwas wirklich teuer ist), und benutze Effects, um „etwas zu tun“ (z. B. fetchen, abonnieren), nicht um „etwas herauszufinden“.
Behandle das Dependency-Array als: „Wenn diese Werte sich ändern, resynce mit der Außenwelt.“ Es ist kein Performance-Trick und kein Ort, um Warnungen zu unterdrücken.
Beispiel: Wenn du Benutzerdetails holst, wenn sich userId ändert, gehört userId ins Dependency-Array, weil es die Synchronisation auslösen soll. Wenn der Effect auch token verwendet, nimm ihn auf, sonst kannst du mit einem alten Token fetchen.
Ein guter Praxistest: Wenn das Entfernen eines Effects nur die UI falsch macht, war es wahrscheinlich kein echter Effect. Wenn das Entfernen dazu führt, dass ein Timer nicht mehr gestoppt wird, eine Subscription nicht gekündigt wird oder ein Fetch nicht ausgeführt wird, war es wahrscheinlich ein echter Effect.
Eines der nützlichsten Mentalmodelle ist simpel: Daten fließen den Baum hinunter, und Nutzeraktionen gehen nach oben.
Ein Parent gibt Werte an Kinder weiter. Kinder sollten nicht heimlich denselben Wert an zwei Stellen „besitzen“. Sie fordern Änderungen an, indem sie eine Funktion aufrufen, und der Parent entscheidet, wie der neue Wert aussieht.
Wenn zwei UI-Teile übereinstimmen müssen, wähle einen Ort, um den Wert zu speichern, und gib ihn nach unten weiter. Das ist „State liften.“ Es kann sich wie zusätzlicher Aufwand anfühlen, aber es verhindert ein schlimmeres Problem: zwei States, die auseinanderlaufen und Hacks erzwingen, um sie synchron zu halten.
Beispiel: eine Suchbox und eine Ergebnisliste. Wenn das Input-Feld seine eigene Query speichert und die Liste ihre eigene Query, wirst du irgendwann sehen: „Das Input zeigt X, aber die Liste benutzt Y.“ Die Lösung ist, query in einem Parent zu halten, ihn an beide weiterzugeben und dem Input ein onChangeQuery(newValue)-Handler zu geben.
State zu liften ist nicht immer die Antwort. Wenn ein Wert nur innerhalb einer Komponente wichtig ist, behalte ihn dort. State nah an der Stelle zu halten, wo er gebraucht wird, macht den Code oft lesbarer.
Eine praktische Grenze:
Wenn du unsicher bist, ob du State liften sollst, suche nach Signalen wie: zwei Komponenten zeigen denselben Wert unterschiedlich; eine Aktion an einer Stelle muss etwas weit entferntes updaten; du kopierst Props in State „nur für den Fall“; oder du fügst Effects hinzu, nur um zwei Werte synchron zu halten.
Dieses Modell hilft auch beim Bauen via Chat-Tools wie Koder.ai: Fordere einen einzigen Owner für jedes Stück geteilten State und generiere Handler, die nach oben fließen.
Wähle ein Feature, das klein genug ist, um es im Kopf zu halten. Ein gutes Beispiel ist eine durchsuchbare Liste, bei der man auf ein Item klicken kann, um Details in einem Modal zu sehen.
Beginne damit, die UI-Teile und die möglichen Events zu skizzieren. Denk noch nicht an Code. Denk daran, was der Benutzer tun kann und was er sehen kann: es gibt ein Suchfeld, eine Liste, eine Hervorhebung der ausgewählten Zeile und ein Modal. Die Events sind Tippen in die Suche, Klick auf ein Item, Öffnen des Modals und Schließen des Modals.
„Zeichne dann den State.“ Schreib die wenigen Werte auf, die gespeichert werden müssen, und entscheide, wer sie besitzt. Eine einfache Regel funktioniert gut: der nächste gemeinsame Parent von allen Orten, die einen Wert brauchen, sollte ihn besitzen.
Für dieses Feature kann der gespeicherte State klein sein: query (String), selectedId (id oder null) und isModalOpen (Boolean). Die Liste liest query und rendert Items. Das Modal liest selectedId, um Details anzuzeigen. Wenn sowohl Liste als auch Modal selectedId brauchen, halte es im Parent, nicht in beiden.
Als Nächstes trenne abgeleitete Daten von gespeicherten Daten. Die gefilterte Liste ist abgeleitet: filteredItems = items.filter(...). Speichere sie nicht im State, weil sie immer aus items und query neu berechnet werden kann. Abgeleitete Daten zu speichern ist der Weg, wie Werte auseinanderlaufen.
Frage dann: brauchen wir einen Effect? Wenn Items bereits im Speicher sind, nein. Wenn das Tippen einer Query Ergebnisse fetchen soll, ja. Wenn das Schließen des Modals etwas speichern soll, ja. Effects sind fürs Synchronisieren (fetch, save, subscribe), nicht fürs grundlegende UI-Wiring.
Teste abschließend den Ablauf mit ein paar Randfällen:
selectedId noch gültig?Wenn du diese Fragen auf Papier beantworten kannst, ist der React-Code meist unkompliziert.
Die meiste React-Verwirrung hat nichts mit Syntax zu tun. Sie entsteht, wenn dein Code aufhört, zur einfachen Geschichte in deinem Kopf zu passen.
Abgeleiteten State speichern. Du speicherst fullName im State, obwohl es nur firstName + lastName ist. Es funktioniert, bis eines der Felder sich ändert und das andere nicht, und die UI zeigt veraltete Werte.
Effect-Loops. Ein Effect holt Daten, setzt State, und die Dependency-Liste lässt ihn wieder laufen. Das Symptom sind wiederholte Requests, wackelige UI oder State, der nie zur Ruhe kommt.
Veraltete Closures. Ein Klick-Handler liest einen alten Wert (z. B. einen veralteten Zähler oder Filter). Das Symptom ist „Ich klickte, aber es benutzte den Wert von gestern.“
Globaler State überall. Wenn du jedes UI-Detail in einen globalen Store packst, wird es schwer herauszufinden, wer was besitzt. Das Symptom ist: Du änderst eine Sache und drei Bildschirme reagieren überraschend.
Verschachtelte Objekte mutieren. Du aktualisierst ein Objekt oder Array in-place und wunderst dich, warum die UI nicht aktualisiert. Das Symptom ist „die Daten haben sich geändert, aber nichts re-rendered.“
Ein konkretes Beispiel: ein „search and sort“-Panel für eine Liste. Wenn du filteredItems im State speicherst, kann es auseinanderdriften, wenn neue Daten ankommen. Stattdessen: speichere die Eingaben (Suchtext, Sortierwahl) und berechne die gefilterte Liste während des Renderns.
Bei Effects: nutze sie für das Synchronisieren mit der Außenwelt (fetching, subscriptions, timer). Wenn ein Effect grundlegende UI-Arbeit macht, gehört sie oft ins Render oder in einen Event-Handler.
Beim Generieren oder Editieren von Code per Chat zeigen sich diese Fehler schneller, weil Änderungen in großen Stücken kommen können. Eine gute Gewohnheit ist, Anfragen in Begriffen von Besitz zu formulieren: „Was ist die Quelle der Wahrheit für diesen Wert?“ und „Können wir das berechnen statt zu speichern?"
Wenn deine UI unvorhersehbar wird, liegt es selten an „zu viel React“. Es ist meist zu viel State, an den falschen Stellen, der Dinge tut, die er nicht tun sollte.
Bevor du ein weiteres useState hinzufügst, halte kurz inne und frag:
Kleines Beispiel: Suchfeld, Filter-Dropdown, Liste. Wenn du sowohl query als auch filteredItems im State hast, hast du zwei Quellen der Wahrheit. Stattdessen behalte query und filter als State und leite filteredItems während des Renderns aus der vollen Liste ab.
Das ist wichtig, auch wenn du schnell über Chat-Tools baust. Geschwindigkeit ist toll, aber frag immer: „Haben wir State hinzugefügt oder versehentlich ein abgeleitetes Feld gespeichert?“ Wenn es abgeleitet ist, lösche diesen State und berechne ihn neu.
Ein kleines Team baut ein Admin-UI: eine Tabelle mit Bestellungen, ein paar Filter und ein Dialog zum Bearbeiten einer Bestellung. Die erste Anforderung ist vage: „Füge Filter und ein Edit-Popup hinzu.“ Das klingt einfach, aber oft endet es mit zufällig verteiltem State.
Mach es konkret, indem du die Anforderung in State und Events übersetzt. Statt „Filter“ nenne den State: query, status, dateRange. Statt „Edit-Popup“ nenne das Event: „User klickt Edit an einer Zeile.“ Dann entscheide, wer welchen State besitzt (Page, Table oder Dialog) und was abgeleitet werden kann (wie die gefilterte Liste).
Beispiel-Prompts, die das Modell intakt halten (funktionieren auch gut in Chat-basierten Buildern wie Koder.ai):
OrdersPage, die filters und selectedOrderId besitzt. OrdersTable wird von filters kontrolliert und ruft onEdit(orderId) auf.“visibleOrders aus orders und filters ab. Speichere visibleOrders nicht im State.“EditOrderDialog hinzu, der order und open erhält. Beim Speichern rufe onSave(updatedOrder) und schließe das Dialog.“filters in die URL zu synchronisieren, nicht um gefilterte Zeilen zu berechnen."Nachdem die UI generiert oder aktualisiert wurde, überprüfe die Änderungen mit einer kurzen Prüfung: jeder State-Wert hat einen Owner, abgeleitete Werte werden nicht gespeichert, Effects synchronisieren nur mit der Außenwelt (URL, Netzwerk, Storage), und Events fließen als Props nach unten und als Callbacks nach oben.
Wenn State vorhersehbar ist, fühlt sich Iteration sicher an. Du kannst Layouts ändern, einen neuen Filter hinzufügen oder Dialogfelder anpassen, ohne zu raten, welcher versteckte State als Nächstes kaputtgeht.
Geschwindigkeit hilft nur, wenn die App weiterhin leicht zu verstehen bleibt. Der einfachste Schutz ist, diese Mentalmodelle wie eine Checkliste zu behandeln, die du anwendest, bevor du UI schreibst (oder generierst).
Beginne jedes Feature gleich: schreibe den benötigten State auf, die Events, die ihn ändern können, und wer ihn besitzt. Wenn du nicht sagen kannst: „Diese Komponente besitzt diesen State, und diese Events aktualisieren ihn“, wirst du wahrscheinlich verstreuten State und überraschende Re-Renders bekommen.
Wenn du über Chat baust, starte im Planungsmodus. Beschreibe Komponenten, State-Shape und Übergänge in einfacher Sprache, bevor du nach Code fragst. Zum Beispiel: „Ein Filter-Panel aktualisiert query-State; die Ergebnisliste leitet sich aus query ab; das Auswählen eines Items setzt selectedId; Schließen leert es.“ Wenn das sauber lesbar ist, wird das Generieren der UI zum mechanischen Schritt.
Wenn du Koder.ai (koder.ai) nutzt, lohnt sich ein kurzer Sanity-Check: ein eindeutiger Owner für jeden State-Wert, UI leitet sich vom State ab, Effects nur für Sync, und keine doppelten Wahrheiten.
Dann iteriere in kleinen Schritten. Wenn du die State-Struktur ändern willst (z. B. mehrere Booleans zu einem einzigen Status-Feld zusammenfassen), mach vorher einen Snapshot, experimentiere und rolle zurück, wenn das mentale Modell schlechter wird. Und wenn du eine tiefere Überprüfung oder Übergabe brauchst, macht das Exportieren des Quellcodes es leichter, die eigentliche Frage zu beantworten: Erzählt die State-Form noch die Geschichte der UI?
Ein guter Einstiegsansatz ist: UI = f(state, props). Deine Komponenten „bearbeiten“ nicht das DOM; sie beschreiben, was für die aktuellen Daten auf dem Bildschirm stehen sollte. Wenn der Bildschirm falsch aussieht, untersuche die State/Props, die ihn erzeugt haben, nicht das DOM.
Props sind Eingaben von einem Parent; deine Komponente sollte sie wie schreibgeschützte Werte behandeln. State ist Erinnerung, die einer Komponente gehört (oder welcher Komponente du auch immer die Verantwortung gibst). Wenn ein Wert geteilt werden muss, heb ihn nach oben und gib ihn als Props weiter.
Ein Re-Render bedeutet, dass React deine Komponentenfunktion erneut ausführt, um die nächste UI-Beschreibung zu berechnen. Es heißt nicht automatisch, dass die gesamte Seite neu gezeichnet wird. React aktualisiert dann das echte DOM mit den minimal nötigen Änderungen.
Weil State-Updates geplant sind und keine sofortigen Zuweisungen. Wenn der nächste Wert vom vorherigen abhängt, nutze die Updater-Form, damit du nicht von einem möglicherweise veralteten Wert ausgehst:
setCount(c => c + 1)Das bleibt korrekt, selbst wenn mehrere Updates in die Warteschlange gestellt werden.
Speichere nichts, was du aus vorhandenen Eingaben berechnen kannst. Bewahre die Eingaben auf und leite den Rest während des Renderns ab.
Beispiele:
items, filtervisibleItems = items.filter(...)So verhinderst du, dass Werte auseinanderlaufen.
Nutze Effects, um mit Dingen zu synchronisieren, die React nicht kontrolliert: Fetches, Subscriptions, Timer, Browser-APIs oder imperatives DOM.\n\nVerwende keinen Effect nur, um UI-Werte aus State zu berechnen—berechne diese während des Renderns (oder mit useMemo, wenn es teuer ist).
Behandle die Dependencies-Liste als eine Trigger-Liste: „Wenn diese Werte sich ändern, re-synce mit der Außenwelt.“ Nimm jeden reaktiven Wert auf, den der Effect liest.
Wenn du etwas weglässt, riskierst du veraltete Daten (z. B. eine alte userId oder einen alten Token). Wenn du falsche Dinge hinzufügst, kannst du Loops erzeugen—oft ein Zeichen, dass die Arbeit eher in Events oder Render gehört.
Wenn zwei Teile der UI immer übereinstimmen müssen, leg den State in ihren nächsten gemeinsamen Parent, gib den Wert nach unten weiter und Callback-Funktionen nach oben.\n\nEin schneller Test: Wenn du denselben Wert in zwei Komponenten duplizierst und Effekte schreibst, um sie „in Sync“ zu halten, braucht dieser State wahrscheinlich einen einzigen Owner.
Das passiert oft, wenn ein Handler einen alten Wert aus einem früheren Render „einfriert“. Häufige Lösungen:
setX(prev => ...)Wenn ein Klick „gestern’s Wert“ benutzt, vermute eine stale closure.
Starte mit einem kleinen Plan: Komponenten, State-Owner und Events. Generiere dann Code als kleine Patches (ein State-Feld hinzufügen, ein Handler hinzufügen, einen Wert ableiten) statt großer Umbauten.
Wenn du einen Chat-Builder wie Koder.ai nutzt, fordere:
Das hält den generierten Code im Einklang mit Reacts mentalem Modell.