Zum Inhalt springen

7.5 React-Grundlagen: Datenfluss und Formulare

Zu Zen-Modus wechseln

Nach dieser Einheit kannst du:

  • den Einweg-Datenfluss von React beschreiben: Props nach unten, Events über Callback-Props nach oben.
  • begründet entscheiden, wo ein Zustand wohnt, und gemeinsam genutzten Zustand zum nächstgemeinsamen Vorfahren heben.
  • Callback-Props typisieren.
  • Formulare als controlled Components mit Validierung bauen und begründen, wann ein uncontrolled Feld mit useRef genügt.

In einer Wohngemeinschaft hängt die Einkaufsliste am Kühlschrank, nicht in der Jackentasche einer Mitbewohnerin. Nur so sehen alle, was fehlt, und wer Milch braucht, schreibt sie dort dazu. Hätte jede ihre eigene Liste, käme die Milch dreimal nach Hause und das Brot gar nicht. In React stellt sich dieselbe Frage bei jedem Entwurf: Wo hängt die Liste? Zustand, den mehrere Komponenten brauchen, hängt beim nächstgemeinsamen Vorfahren, und die Kinder melden Änderungen dorthin.

flowchart TD
    accTitle: Gemeinsamer Zustand wohnt beim gemeinsamen Vorfahren
    fridge["JobDashboard<br/>hält jobs und searchText"] -- "searchText, onSearch" --> filter["FilterField"]
    fridge -- "gefilterte jobs, onCancel" --> list["JobList"]
    fridge -- "jobs.length" --> header["Header"]

Abb. 7.20: Die “Einkaufsliste” hängt beim gemeinsamen Vorfahren, die Kinder lesen und melden (eigene Darstellung).

In Einheit 7.4 hatte jede Komponente ihren privaten State. Diese Einheit verbindet Komponenten: Sie zeigt, wie Daten durch die Hierarchie fließen und wie Formulare Benutzereingaben in den State bringen.

  • Einweg-Datenfluss: Daten fließen in React nur von Eltern- zu Kindkomponenten.
  • Callback-Prop: ein Prop, dessen Wert eine Funktion ist, über die ein Kind den Eltern etwas meldet, etwa onCancel.
  • State heben (lifting state up): Zustand aus einem Kind in den gemeinsamen Vorfahren verschieben, damit mehrere Komponenten ihn nutzen.
  • Controlled Component: ein Formularfeld, dessen Wert aus dem React-State kommt.
  • Ref: eine Referenz auf ein DOM-Element, über die React ein Feld bei Bedarf ausliest.

In React fließen Daten in eine Richtung: von Eltern- zu Kindkomponenten über Props. Will ein Kind etwas ändern, ruft es eine Funktion auf, die ihm die Eltern als Prop mitgegeben haben:

flowchart TD
    accTitle: Props runter, Events rauf am Beispiel JobDashboard
    parent["JobDashboard<br/>hält den State"] -- "Props runter: jobs, onCancel" --> child["JobList"]
    child -- "Events rauf: onCancel(id)" --> parent

Abb. 7.21: Die Daten fließen nach unten, die Meldung über die Funktion nach oben (eigene Darstellung).

function JobDashboard() {
const [jobs, setJobs] = useState<Job[]>(initialJobs);
function cancelJob(id: number) {
setJobs(jobs.filter((j) => j.id !== id));
}
return <JobList jobs={jobs} onCancel={cancelJob} />;
}
function JobList({ jobs, onCancel }: { jobs: Job[]; onCancel: (id: number) => void }) {
return (
<ul>
{jobs.map((job) => (
<li key={job.id}>
{job.filename}
<button onClick={() => onCancel(job.id)}>cancel</button>
</li>
))}
</ul>
);
}

Beispielprojekt: 07_react_grundlagen_examples/ex16_job_dashboard

JobList kennt den State nicht und ändert ihn nicht; sie bekommt die Liste und eine Funktion. Den eigentlichen Zustand ändert nur JobDashboard, über ihren Setter.

Damit stellt sich bei jedem Entwurf dieselbe Frage: Wo lebt welcher Zustand? Die Antwort folgt einer Regel: beim nächstgemeinsamen Vorfahren aller Komponenten, die ihn brauchen. Brauchen Filterfeld und Liste denselben Suchtext, gehört er in die gemeinsame Elternkomponente und wandert als Prop nach unten (lifting state up). Zustand, den nur eine Komponente braucht, bleibt bei ihr. Diese Entwurfsfrage entscheidet mehr über die Qualität einer React-Anwendung als jede Syntax.

Beachte die Typisierung der Callback-Prop: onCancel: (id: number) => void ist der Vertrag in beide Richtungen. Die Elternkomponente muss eine passende Funktion liefern, das Kind darf sie nur mit einer Zahl aufrufen; beides prüft der Compiler, bevor die Seite lädt.

Wo begegnet dir das? In jedem Webshop mit Warenkorb-Symbol in der Kopfzeile: Legst du in der Produktliste etwas in den Warenkorb, ändert sich die Zahl oben sofort. Kopfzeile und Produktliste sind Geschwister, also wohnt der Warenkorb in einem gemeinsamen Vorfahren.

Auflösung

Der Suchtext wohnt in App, dem nächstgemeinsamen Vorfahren. FilterField bekommt searchText und onSearchChange, JobList bekommt die gefilterte Liste (in App beim Rendern berechnet) und Header deren Länge. Die gefilterte Liste ist kein eigener State, sondern wird aus Aufträgen und Suchtext abgeleitet.

Formulare sind das Haupteinfallstor für Benutzereingaben und in React ein eigenes Thema, weil es zwei legitime Ansätze gibt.

Beim kontrollierten Formularfeld ist React die einzige Quelle der Wahrheit: Der Feldwert kommt aus dem State (value), jede Eingabe läuft über onChange in den State zurück.

function NewJobForm({ onCreate }: { onCreate: (job: NewJob) => void }) {
const [filename, setFilename] = useState("");
const [preset, setPreset] = useState("web-1080p");
function handleSubmit(event: React.FormEvent) {
event.preventDefault(); // stop the browser's page reload
if (filename.trim() === "") return;
onCreate({ filename: filename.trim(), preset });
setFilename(""); // reset the field
}
return (
<form onSubmit={handleSubmit}>
<input value={filename} onChange={(e) => setFilename(e.target.value)} />
<select value={preset} onChange={(e) => setPreset(e.target.value)}>
<option value="web-1080p">Web 1080p</option>
<option value="web-4k">Web 4K</option>
</select>
<button type="submit" disabled={filename.trim() === ""}>Create job</button>
</form>
);
}

Beispielprojekt: 07_react_grundlagen_examples/ex17_new_job_form

NewJob ist der Eingabetyp aus Kapitel 6: Pick<Job, "filename" | "preset">. Der Preis des kontrollierten Felds sind ein State und ein Handler pro Feld. Der Gewinn ist Kontrolle in Echtzeit: Der Knopf ist deaktiviert, solange die Eingabe ungültig ist; eine Live-Validierung oder Zeichenzählung wäre eine Zeile; der State ist jederzeit der komplette, aktuelle Formularinhalt.

flowchart LR
    accTitle: Der Kreislauf eines kontrollierten Felds
    state["State<br/>filename"] -- "value" --> input["input"]
    input -- "onChange" --> setter["setFilename(...)"]
    setter --> state

Abb. 7.22: Beim kontrollierten Feld fließt jeder Tastendruck durch den State zurück ins Feld (eigene Darstellung).

Zwei Details: event.preventDefault() im Submit-Handler verhindert das Neuladen der Seite (das Standardverhalten von HTML-Formularen), und Checkboxen lesen sich über e.target.checked statt value.

Beim unkontrollierten Feld verwaltet der Browser den Wert selbst, wie in klassischem HTML; React liest ihn nur bei Bedarf aus, über eine Ref auf das DOM-Element. useRef ist ein weiterer Hook; Einheit 7.6 stellt ihn genauer vor.

function SearchForm({ onSearch }: { onSearch: (term: string) => void }) {
const inputRef = useRef<HTMLInputElement>(null);
function handleSubmit(event: React.FormEvent) {
event.preventDefault();
onSearch(inputRef.current?.value ?? "");
}
return (
<form onSubmit={handleSubmit}>
<input ref={inputRef} defaultValue="" placeholder="search" />
<button type="submit">Search</button>
</form>
);
}

Beispielprojekt: 07_react_grundlagen_examples/ex18_search_form

Statt value gibt es höchstens ein defaultValue (den Startwert); kein State, kein onChange, kein Neu-Rendern pro Tastendruck.

Kriteriumcontrolleduncontrolled
Wert liegt inReact-StateDOM
Live-Validierung, abhängige Feldernatürlichumständlich
Feld zurücksetzen oder programmatisch fülleneinfach (Setter)über die Ref
Aufwand pro FeldState + Handlereine Ref
Neu-Rendern bei jedem Tastendruckjanein

Faustregel: Controlled ist der Standard dieses Jahres, weil Validierung und Reaktionen auf Eingaben ständig gebraucht werden. Uncontrolled ist die berechtigte Abkürzung für einfache Fälle nach dem Muster “ein Feld, ein Absenden, dazwischen egal”. Wichtig ist, sich pro Feld zu entscheiden: Ein value ohne onChange erzeugt ein eingefrorenes Feld (React warnt in der Konsole), und das nachträgliche Umschalten zwischen beiden Modi quittiert React ebenfalls mit einer Warnung.

Auflösung

(a), (c) und (d) controlled: Der Zähler und der aktive Knopf leiten sich bei jedem Tastendruck aus dem State ab, und Leeren ist ein Setter-Aufruf. (b) darf uncontrolled sein: ein Feld, ein Absenden, dazwischen keine Reaktion nötig. Controlled wäre auch dort richtig, nur etwas mehr Code.

  1. Zustand im falschen Kind: Filterfeld und Liste haben je einen eigenen Suchtext, die beiden laufen auseinander. Der Zustand gehört in den gemeinsamen Vorfahren.
  2. Callback im Rumpf aufgerufen: onCancel(job.id) direkt im JSX statt in () => onCancel(job.id) läuft bei jedem Rendern.
  3. value ohne onChange: Das Feld ist eingefroren; React warnt in der Konsole.
  4. preventDefault() vergessen: Das Formular lädt die Seite neu, der State ist weg.
  5. Gefilterte Liste als eigener State: Sie lässt sich aus Liste und Suchtext berechnen und läuft sonst auseinander.

Du kannst dir diese Einheit kostenlos mit der Vorlesefunktion deines Browsers oder Betriebssystems vorlesen lassen (etwa „Laut vorlesen” in Edge), ohne Konto und ohne Upload; die Codebeispiele liest du dabei am Bildschirm mit. Freiwillig kannst du mit Google NotebookLM (Google-Konto nötig) aus diesem öffentlichen Text eine gesprochene Zusammenfassung erzeugen, auch auf Deutsch.

Du kannst jetzt:

  • den Einweg-Datenfluss von React beschreiben: Props nach unten, Events über Callback-Props nach oben.
  • begründet entscheiden, wo ein Zustand wohnt, und gemeinsam genutzten Zustand zum nächstgemeinsamen Vorfahren heben.
  • Callback-Props typisieren.
  • Formulare als controlled Components mit Validierung bauen und begründen, wann ein uncontrolled Feld mit useRef genügt.
  • Aufgabe 07-4a - State-Training, Stufen Wesentlich_vollständig, PlusPlus und MandalorianPlus
  • Einweg-Datenfluss, State heben → react.dev: Sharing state between components, Thinking in React
  • Controlled und uncontrolled Felder, defaultValue, Warnungen → react.dev: <input>
  • useRef → react.dev: useRef
  • preventDefault → MDN: Event: preventDefault() method
  • Warnung bei value ohne onChange → eigener Test im Beispielprojekt ex17_new_job_form (Oktober 2026)
  • Abb. 7.20 (gemeinsamer Vorfahr), Abb. 7.21 (Props runter, Events rauf), Abb. 7.22 (kontrolliertes Feld) → eigene Darstellung, alle als Mermaid-Diagramm
  • Oliver Maurhart (MAO)
  • Michael Gabriel (GAM)

CC-BY-SA

https://qiz.at/rules