React-Überblick: Von JSX zur Komponentenarchitektur
Eine große Landkarte von React, wie ich es heute unterrichte – vom deklarativen Rendern über die Frage, wem ein Zustand gehört, bis zur Architektur einer wachsenden Anwendung. Die Details zu einzelnen Stationen kommen später als eigene Beiträge.
React-Überblick: Von JSX zur Komponentenarchitektur
Wenn ich eine React-Schulung gebe, kommt irgendwann die Frage, die eigentlich alles zusammenhält: Wo gehört dieser Zustand hin? Sie klingt harmlos und entscheidet trotzdem über die halbe Architektur einer Anwendung. Dieser Beitrag ist der Versuch, die ganze Landkarte einmal am Stück zu zeichnen – vom ersten Stück JSX bis zu der Stelle, an der aus einzelnen Komponenten ein System wird. Es ist bewusst ein Überblick. Zu den meisten Stationen, die ich hier nur kurz betrete, folgen später eigene Beiträge, die tiefer graben. Heute geht es darum, die Karte im Kopf zu haben, bevor man sich in ein Detail verliert.
Ich stütze mich dabei auf zwei Übungsrepositories, die ich seit Jahren in Schulungen benutze und gerade zusammenführe: introduction-react für die Grundlagen und advanced-react für die fortgeschrittenen Muster. Beide sind kein Foliensatz in Textform, sondern lauffähiger Code, an dem wir gemeinsam üben. Der Stand, den ich hier beschreibe, ist React 19 – die stabile Version von Ende 2024, mit allem, was seither zum Alltag geworden ist.
Das mentale Modell: die Oberfläche ist eine Funktion des Zustands
Bevor eine einzige Zeile JSX Sinn ergibt, muss ein Gedanke sitzen. React dreht die Art um, wie man über eine Oberfläche nachdenkt. Statt Schritt für Schritt zu beschreiben, wie sich das DOM verändern soll – hier ein Element einfügen, dort ein Attribut ändern, da einen Knoten entfernen –, beschreibt man nur, wie die Oberfläche für einen gegebenen Zustand aussehen soll. React kümmert sich um den Weg dorthin.
Man kann das in eine einzige Zeile fassen, die ich in der Schulung an die Wand schreibe und die den ganzen Rest trägt: Die Oberfläche ist eine Funktion des Zustands. Ändert sich der Zustand, ruft React die Funktion erneut auf, vergleicht das Ergebnis mit dem vorherigen und schreibt nur die tatsächlichen Unterschiede ins echte DOM. Dieser Abgleich läuft über eine interne, leichtgewichtige Repräsentation des Baums, oft Virtual DOM genannt. Der Name ist weniger wichtig als die Konsequenz: Man beschreibt das Ziel, nicht den Weg.
flowchart LR State["Zustand (state)"] --> Render["Komponente rendert"] Render --> View["beschriebene Oberfläche"] View --> DOM["React gleicht ab und schreibt ins DOM"] User["Nutzerinteraktion"] --> Event["Event / setState"] Event --> State
Der zweite tragende Gedanke ist der unidirektionale Datenfluss. Daten fließen von oben nach unten, von der Elternkomponente zum Kind, über Props. Ereignisse fließen in die andere Richtung, als Rückrufe nach oben. Es gibt keine geheimnisvolle Zweiwegbindung, bei der ein Eingabefeld und eine Variable magisch synchron bleiben. Das wirkt am Anfang umständlicher als in älteren Frameworks, aber es ist der Grund, warum sich React-Anwendungen auch nach zwei Jahren noch erklären lassen: Man kann für jede Stelle der Oberfläche sagen, woher ihre Daten kommen und wer sie ändern darf.
JSX und Komponenten: die kleinste Einheit ist eine Funktion
JSX sieht aus wie HTML im JavaScript, ist aber nur eine bequeme Schreibweise für Funktionsaufrufe. Ein <button> wird zu einem Aufruf, der ein Objekt beschreibt, kein DOM-Element erzeugt. Deshalb kann man mitten im JSX rechnen, Listen abbilden und bedingt rendern – es ist ja alles nur JavaScript:
function Rechnungsliste({ rechnungen }) {
if (rechnungen.length === 0) return <p>Keine Rechnungen.</p>;
return (
<ul>
{rechnungen.map((r) => (
<li key={r.id}>
{r.nummer} - {r.bezahlt ? "bezahlt" : "offen"}
</li>
))}
</ul>
);
}
Drei Details in diesem kleinen Beispiel sind in Wahrheit Architekturthemen. Das key-Attribut ist kein kosmetisches Detail, sondern die Information, an der React beim erneuten Rendern erkennt, welches Listenelement welchem entspricht. Das bedingte Rendern mit ? oder mit einem frühen return ist die Stelle, an der Lade-, Fehler- und Leerzustände sichtbar werden – genau die Zustände, die im Demo-Modus gern unterschlagen werden und im Betrieb dann fehlen. Und props ist eine schreibgeschützte Schnittstelle: Eine Komponente bekommt ihre Daten von außen und darf sie nicht verändern. Das macht Komponenten zu Bausteinen, deren Verhalten man von außen versteht.
Der Default ist heute die Funktionskomponente. Sie ist eine Funktion, die Props entgegennimmt und JSX zurückgibt, und mit Hooks kann sie inzwischen alles, wofür man früher Klassen brauchte. Klassenkomponenten mit render-Methode und Lifecycle-Methoden wie componentDidMount existieren noch, und man trifft sie in älterem Code. In der Schulung zeige ich sie kurz, als historische Schicht, damit niemand erschrickt, wenn er sie sieht. Aber neuer Code entsteht mit Funktionen. Das ist kein Modegeschmack, sondern eine echte Vereinfachung: Ein Effekt, der beim Einhängen und beim Aushängen etwas tun soll, war früher über zwei getrennte Lifecycle-Methoden verstreut. Heute steht er an einer Stelle.
Komposition ersetzt Vererbung. Statt eine Basiskomponente zu erweitern, steckt man Komponenten ineinander und reicht Inhalt über children durch:
function Karte({ titel, children }) {
return (
<section className="karte">
<h2>{titel}</h2>
{children}
</section>
);
}
Eine solche Karte weiß nichts über ihren Inhalt und ist gerade deshalb überall verwendbar. Wer diesen Reflex verinnerlicht – lieber zusammenstecken als vererben –, schreibt fast automatisch entkoppelte Oberflächen.
Zustand ist kein Ort
Jetzt kommt die Frage vom Anfang, und sie ist die wichtigste im ganzen Beitrag. Zustand ist kein Ort im Code, sondern eine Entscheidung über Besitz, Lebensdauer und Synchronisation. Bevor man useState schreibt, sollte man wissen, wem dieser Zustand eigentlich gehört.
Der lokale Fall ist der einfachste. Ein Zähler, ein aufgeklapptes Menü, der Text in einem Eingabefeld – solcher Zustand gehört in die Komponente, die ihn anzeigt:
import { useState } from "react";
function Zaehler({ start = 0 }) {
const [wert, setWert] = useState(start);
return <button onClick={() => setWert((n) => n + 1)}>{wert}</button>;
}
Die Regel, die React an dieser Stelle einfordert, heißt: Komponenten rein halten. Für dieselben Eingaben soll dieselbe Ausgabe herauskommen. Der Zustand wird nicht direkt verändert, sondern über die Setter-Funktion ersetzt, und React entscheidet, wann neu gerendert wird. Wer das ernst nimmt, bekommt Komponenten, die man ohne laufenden Browser im Kopf durchspielen kann.
Interessant wird es, sobald ein Zustand nicht mehr in eine einzelne Komponente passt. Dann muss man ehrlich beantworten, wo er hingehört, und die Antwort ist selten „in einen globalen Store". Ich unterscheide in der Schulung vier Orte, und die Wahl zwischen ihnen ist die eigentliche Architekturarbeit im Frontend:
flowchart TB Frage["Wem gehört dieser Zustand?"] Frage --> Lokal["Komponente<br/>(useState) – UI-Detail"] Frage --> URL["URL<br/>Filter, Seite, ausgewähltes Objekt"] Frage --> Server["Server-Cache<br/>geladene Daten, die dem Backend gehören"] Frage --> Global["globaler Store<br/>app-weite, geteilte Entscheidungen"]
Vieles, was Teams reflexhaft in einen globalen Store legen, gehört eigentlich in die URL – welcher Filter aktiv ist, welche Seite angezeigt wird, welches Objekt ausgewählt ist. In der URL ist dieser Zustand teilbar, mit einem Lesezeichen speicherbar und überlebt einen Reload. Und ein großer Teil dessen, was wie Anwendungszustand aussieht, ist in Wahrheit gar keiner: Es sind Daten, die dem Server gehören und die man nur zwischengespeichert hat. Diese Unterscheidung zwischen echtem Client-Zustand und gecachten Serverdaten ist die folgenreichste im ganzen Frontend, und ich widme ihr später einen eigenen Beitrag.
Effekte sind der Rand, nicht die Mitte
useEffect ist der Hook, der in Schulungen die meisten Missverständnisse produziert. Er synchronisiert eine Komponente mit etwas außerhalb von React – einem Netzwerkaufruf, einem Abonnement, dem Titel des Browsertabs, einem Timer. Der entscheidende Satz lautet: Ein Effekt ist der Rand des Systems, nicht seine Mitte. Alles, was sich aus dem Zustand berechnen lässt, gehört nicht in einen Effekt, sondern direkt ins Rendern.
import { useState, useEffect } from "react";
function useRechnung(id) {
const [daten, setDaten] = useState(null);
const [fehler, setFehler] = useState(null);
useEffect(() => {
let aktiv = true;
fetch(`/api/rechnungen/${id}`)
.then((r) => r.json())
.then((d) => aktiv && setDaten(d))
.catch((e) => aktiv && setFehler(e.message));
return () => {
aktiv = false;
};
}, [id]);
return { daten, fehler };
}
Zwei Dinge an diesem Beispiel sind die eigentliche Lektion. Das Abhängigkeits-Array [id] bestimmt, wann der Effekt erneut läuft – vergisst man dort einen Wert, arbeitet die Komponente mit veralteten Daten weiter. Und die zurückgegebene Aufräumfunktion, die aktiv auf false setzt, verhindert, dass eine spät eintreffende Antwort einen bereits ausgehängten oder inzwischen überholten Zustand überschreibt. Genau dieser Wettlauf zwischen zwei Anfragen ist der Fehler, den man im Test mit schnellen Antworten nie sieht und im Betrieb mit langsamem Netz sehr wohl.
Ich habe die Logik hier bewusst in einen eigenen Custom Hook useRechnung gezogen. Damit sind wir schon beim heutigen Weg, Logik wiederzuverwenden – dazu gleich mehr. Wichtig ist an dieser Stelle nur die Haltung: Effekte begrenzen, nicht häufen. Ein Frontend, in dem Datenladen, Ableitung, Synchronisation und Nutzerereignisse in einem einzigen Effektklumpen landen, ist fragil, weil niemand mehr sagen kann, warum welcher Effekt wann läuft.
Der häufigste Fehler, den ich in der Schulung sehe, ist genau der umgekehrte Reflex: einen Wert, der sich aus anderem Zustand berechnen lässt, über einen Effekt in einen zweiten Zustand zu spiegeln. Wer den vollständigen Namen aus Vor- und Nachnamen per Effekt in eine eigene Variable schreibt, hat sich zwei Quellen der Wahrheit gebaut, die auseinanderlaufen, sobald ein Rendern dazwischenkommt. Der berechnete Wert gehört nicht in einen Effekt und auch nicht in einen zweiten useState, sondern schlicht ins Rendern, wo er bei jedem Durchlauf frisch entsteht. Diese Faustregel – alles, was ableitbar ist, wird abgeleitet und nicht gespeichert – erspart mehr Fehler als jede Bibliothek. Ein Effekt ist erst dann berechtigt, wenn wirklich etwas außerhalb von React im Spiel ist.
Für das Laden von Serverdaten muss man diesen Mechanismus in echten Projekten übrigens selten von Hand schreiben. In der Praxis übernimmt eine darauf spezialisierte Bibliothek das Zwischenspeichern, das erneute Laden und die Konsistenz. React 19 geht mit dem use()-Hook und Suspense noch einen Schritt weiter und erlaubt es, ein Promise direkt im Rendern abzuwarten, sodass Lade- und Fehlerzustände weiter nach außen an eine umschließende Grenze wandern. Das ist ein eigenes großes Thema, das ich hier nur auf die Landkarte setze.
Von HOCs und Render Props zu Custom Hooks
Die fortgeschrittenen React-Muster, die im advanced-react-Material einen eigenen Block bilden, sind fast alle Antworten auf dieselbe Frage: Wie teile ich Logik zwischen Komponenten, ohne sie zu kopieren? Die Antwort hat sich über die Jahre verschoben, und dieser Wandel ist selbst lehrreich.
Früher gab es dafür zwei etablierte Muster. Eine Higher-Order Component ist eine Funktion, die eine Komponente entgegennimmt und eine erweiterte zurückgibt – ein Wrapper, der Verhalten hinzufügt. Render Props lösen dasselbe Problem, indem eine Komponente ihre Logik über eine Funktion als Kind nach außen gibt. Beide funktionieren, aber beide haben denselben Preis: Sie verschachteln den Komponentenbaum und machen aus einer flachen Idee eine tiefe Struktur.
Custom Hooks lösen genau dieses Problem, ohne den Baum zu verschachteln. Ein Custom Hook ist einfach eine Funktion, deren Name mit use beginnt und die andere Hooks benutzt. Das useRechnung von oben ist bereits einer. Die Logik lebt in einer normalen Funktion, wird von mehreren Komponenten aufgerufen und taucht im Baum überhaupt nicht auf:
function Rechnungsansicht({ id }) {
const { daten, fehler } = useRechnung(id);
if (fehler) return <Fehlermeldung text={fehler} />;
if (!daten) return <Ladeanzeige />;
return <Rechnungsdetails rechnung={daten} />;
}
Dieselbe Bewegung findet man beim Kontext. Reichen Daten über viele Ebenen durch, ohne sie durch jede Zwischenkomponente als Prop zu fädeln, ist das die Aufgabe von Context: ein Provider oben im Baum, ein useContext unten. Der wichtige Hinweis aus dem Schulungsmaterial lautet, dass Context zum Verteilen gedacht ist, nicht als Ersatz für einen durchdachten Zustandsbesitz. Wer jeden veränderlichen Wert in einen Context legt, verlagert das Kopplungsproblem nur eine Ebene höher. Auch das – Context als Verteilweg gegen Context als heimlicher globaler Zustand – verdient einen eigenen, genaueren Beitrag.
Wenn geteilter Zustand wirklich global ist: Redux und die Alternativen
Manchmal ist Zustand tatsächlich app-weit und wird von vielen, weit auseinanderliegenden Stellen gelesen und geändert. Für diesen Fall gibt es etablierte Werkzeuge, und Redux ist das bekannteste. Sein Kern ist erstaunlich klein: Der gesamte Zustand liegt in einem Store, geändert wird er nur durch das Verschicken einer Action – einem einfachen Objekt mit einem type –, und ein Reducer ist eine pure Funktion, die aus altem Zustand und Action den neuen Zustand berechnet.
function rechnungenReducer(state = [], action) {
switch (action.type) {
case "rechnung/bezahlt":
return state.map((r) =>
r.id === action.id ? { ...r, bezahlt: true } : r
);
default:
return state;
}
}
Der Reiz dieses Musters ist, dass jede Zustandsänderung ein benanntes, protokollierbares Ereignis wird. Man kann die Abfolge der Actions nachvollziehen, aufzeichnen und im Test durchspielen. Der Preis ist eine gewisse Menge an Struktur. In der Schulung zeige ich drei Arten, diese Struktur zu organisieren, weil die Wahl darüber entscheidet, ob ein UI-System fachlich oder technisch wächst: den Rails-Stil mit getrennten Ordnern für Actions, Reducer und Container; den Domain-Stil, der stattdessen pro Feature bündelt; und das Ducks-Muster, das Actions und Reducer einer Sache in eine Datei legt. Der Domain-Schnitt ist fast immer der bessere, weil er die Datei-Grenzen entlang der Fachlichkeit zieht und nicht entlang technischer Rollen.
Heute schreibt kaum jemand Redux mehr von Hand so aus. Das offizielle Redux Toolkit nimmt einem den größten Teil der Wiederholung ab, und für viele Anwendungen reicht eine leichtere Bibliothek oder schlicht Context mit einem Reducer. Reaktive Ansätze wie MobX verfolgen dasselbe Ziel mit einem anderen Modell, bei dem sich abgeleitete Werte automatisch aktualisieren. Welches dieser Werkzeuge passt, hängt weniger von der Mode ab als von der ehrlichen Antwort auf die Besitzfrage von vorhin – und ist ein weiteres Thema, das einen eigenen Vergleich verdient.
Ich mache in Schulungen gern die Gegenprobe: Für wie viel des vermeintlich globalen Zustands braucht man den Store wirklich? Meistens schrumpft die Liste dramatisch, sobald man die URL für Auswahl und Filter nutzt und geladene Serverdaten als das behandelt, was sie sind – Cache, nicht Zustand. Übrig bleibt oft nur ein kleiner Kern echter, app-weiter Entscheidungen: die angemeldete Person, das gewählte Sprach- oder Theme-Setting, vielleicht ein Warenkorb. Für diesen Kern lohnt sich ein Store, für den Rest ist er Ballast. Ein globaler Store ist also nicht die Standardantwort, sondern die Ausnahme, die man begründen können muss.
Router, Server und die Ränder der Anwendung
Zwei Ränder fehlen noch auf der Karte. Der erste ist die Navigation. In einer Single-Page-Anwendung wechselt man nicht die Seite, sondern bildet URLs auf Komponenten ab. Der Router hält Adresszeile und angezeigte Komponente synchron, sodass Vor- und Zurück-Knopf, Lesezeichen und geteilte Links weiter funktionieren:
import { BrowserRouter, Routes, Route } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Start />} />
<Route path="/rechnungen/:id" element={<Rechnungsansicht />} />
<Route path="*" element={<NichtGefunden />} />
</Routes>
</BrowserRouter>
);
}
Der Router ist damit auch der natürliche Ort für den Zustand, der in die URL gehört. Der Parameter :id, den man in der Komponente über useParams ausliest, ist genau das ausgewählte Objekt, über das ich weiter oben gesprochen habe – kein Grund, es zusätzlich in einem Store zu halten. Für neue Projekte empfiehlt React Router inzwischen die Data-Router-Variante mit createBrowserRouter, bei der Routen zusätzlich Lade- und Schreibfunktionen bekommen; das ist der modernere Weg und ein Thema für sich.
Der zweite Rand ist das Rendern auf dem Server. Für Startzeit, Suchmaschinen und schwache Geräte ist es oft sinnvoll, die erste Fassung der Seite auf dem Server zu erzeugen und im Browser nur noch lebendig zu machen. Frameworks wie Next.js nehmen einem diese Mechanik ab. React 19 hat das Thema mit den Server Components noch einmal grundlegend erweitert: Komponenten können auf dem Server laufen, dort Daten laden und nur ihr Ergebnis an den Browser schicken, während die Server Actions das Gegenstück für schreibende Vorgänge bilden. Das verschiebt die alte Grenze zwischen Client und Server spürbar, und es ist ein so großes Thema, dass es hier bewusst nur als Wegweiser steht.
Betrieb: Tests, Werkzeugkette, Typen
Eine Landkarte, die beim Rendern aufhört, wäre unvollständig, denn all die Grenzen, die ich beschrieben habe – Zustandsbesitz, Effekt-Disziplin, Datenfluss –, lassen sich unbemerkt kaputtmachen, wenn niemand eine Erwartung daran festhält. Tests sind im Frontend keine Pflichtübung, sondern der Ort, an dem diese Erwartungen sichtbar bleiben.
Ich teste Komponenten heute mit Vitest und der React Testing Library, und deren Leitidee passt genau zur Architektur dieses Beitrags: Man prüft, was Nutzer sehen und tun, nicht wie die Komponente innen gebaut ist. Ein Test, der an das sichtbare Ergebnis gebunden ist, überlebt ein Refactoring; ein Test, der an Implementierungsdetails klebt, bricht bei jeder Umbenennung.
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Zaehler } from "./Zaehler";
describe("Zaehler", () => {
it("zählt bei Klick hoch", async () => {
render(<Zaehler start={0} />);
const knopf = screen.getByRole("button", { name: "0" });
await userEvent.click(knopf);
expect(screen.getByRole("button", { name: "1" })).toBeInTheDocument();
});
});
Dahinter steht dieselbe Testpyramide wie überall: viele schnelle Komponententests an der Basis, weniger Integrationstests in der Mitte, eine Handvoll Ende-zu-Ende-Tests mit einem Werkzeug wie Playwright an der Spitze. Wer diese Verteilung umdreht, merkt es an der Laufzeit der Build-Pipeline, wenn es zu spät ist.
Auch die Werkzeugkette ist eine Architekturentscheidung, keine Geschmacksfrage. Ob ein Projekt mit Vite, Next.js, Parcel oder einer handgeschriebenen Webpack-Konfiguration startet, legt fest, wie schnell die Rückmeldung beim Entwickeln ist, wie einfach das Server-Rendern wird und wie viel man selbst wartet. Und über all dem liegt TypeScript, das aus Props und Zustand überprüfbare Verträge macht. Ein falsch getippter Prop wird dann nicht zum Laufzeitfehler beim Nutzer, sondern zum roten Kringel im Editor. Der React Compiler, den das React-Team als eigenes Projekt neben der Bibliothek entwickelt, nimmt einem schließlich einen Teil der manuellen Optimierung ab: Er analysiert Komponenten beim Bauen und merkt sich automatisch, was sich zwischen zwei Renderdurchläufen nicht geändert hat, sodass viele Aufrufe von useMemo, useCallback und memo überflüssig werden.
Das Übungsrepo ist selbst schon Architektur
Ein Punkt, den ich Teilnehmern gern zum Schluss zeige, ist die Struktur der Übungsrepositories selbst. introduction-react und advanced-react sind nicht ein großer Klumpen, sondern viele kleine, eigenständige Beispiele – eine Basis-App, eine Redux-App, eine Router-App, eine mit Server-Rendern, dazu die fortgeschrittenen Ordner für asynchrone Muster, funktionale Komposition und Struktur. Jedes Beispiel steht für sich und lässt sich einzeln starten.
Dass ich die beiden Repositories gerade zusammenlege, ist deshalb selbst eine kleine Architekturübung. Die Frage ist dieselbe wie bei jedem wachsenden System: Was darf geteilt werden, und was muss getrennt bleiben, damit eine Änderung an einer Stelle nicht überraschend an einer anderen etwas kaputt macht? Ein sauber geschnittenes Monorepo aus Grundlagen- und Fortgeschrittenen-Beispielen löst genau dieselbe Aufgabe wie eine Anwendung, die aus vielen Komponenten und Modulen besteht. Die Grenzen, die man zieht, sind an beiden Stellen die eigentliche Arbeit.
flowchart TB Repo["React-Übungsmonorepo"] Repo --> Grund["Grundlagen<br/>JSX, Komponenten, Hooks, Router"] Repo --> Fort["Fortgeschritten<br/>async, funktional, Struktur"] Repo --> Geteilt["geteilte Bausteine<br/>UI-Komponenten, Testhilfen"] Grund --> Geteilt Fort --> Geteilt
Was am Ende bleiben soll
Wenn ich am letzten Schulungstag frage, was hängen geblieben ist, freut mich eine bestimmte Antwort am meisten. Nicht „ich kenne jetzt die Hooks", sondern eher: „Ich frage mich jetzt bei jedem Zustand zuerst, wem er gehört und wie lange er leben soll." Genau das ist der Kern. React gibt einem ein sehr gutes Werkzeug, um Oberflächen zu beschreiben, aber es trifft die Architekturentscheidungen nicht für einen. Wo ein Zustand lebt, wo ein Effekt seinen Rand hat, welche Grenze in der URL, im Server-Cache oder im Store liegt – das bleibt die Arbeit, die ein Mensch machen muss.
Deshalb ist dieser Beitrag ein Überblick und kein Handbuch. Ich habe viele Stationen nur kurz betreten: den Unterschied zwischen Client-Zustand und Server-Cache, Context als Verteilweg, die Wahl zwischen Redux und seinen leichteren Alternativen, Suspense und Server Components, das Testen ohne Theater. Zu jeder dieser Stationen gibt es mehr zu sagen, als in eine Landkarte passt, und genau das ist der Plan: Die Details folgen als eigene Beiträge, jeder mit einem konkreten Beispiel und mindestens einem Fehlerfall. Wer die Karte im Kopf hat, weiß dann, an welcher Stelle er gerade steht.
Weiterführende Quellen
- Übungsrepository introduction-react (Grundlagen): https://github.com/mikebild/introduction-react
- Übungsrepository advanced-react (fortgeschrittene Muster): https://github.com/mikebild/advanced-react
- Folienübersicht zu diesem Überblick: https://supabase.mikebild.dev/storage/v1/object/public/slides/introduction-react.html
- React, offizielle Dokumentation (Thinking in React): https://react.dev/learn/thinking-in-react
- React 19, Ankündigung der stabilen Version: https://react.dev/blog/2024/12/05/react-19
- React, State verwalten und teilen: https://react.dev/learn/managing-state
Kommentare
Kommentar schreiben