Was ist React?
React ist eine JavaScript-Bibliothek zum Erstellen von Benutzeroberflächen aus Komponenten. Eine Komponente ist eine Funktion, die Markup zurückgibt, und Sie setzen diese Komponenten zusammen, um eine Seite aufzubauen. Anstatt dem Browser Schritt für Schritt vorzugeben, wie er das DOM ändern soll, beschreiben Sie, wie die UI basierend auf den aktuellen Daten aussehen soll, und React übernimmt die entsprechenden Aktualisierungen.
Dieser Ansatz wird als deklaratives Rendering bezeichnet und ist der Grund, warum sich React im Jahr 2013 so anders anfühlte. Man hört auf, Anweisungen wie „finde dieses Element, ändere seinen Text, schalte diese Klasse um“ zu schreiben, und beginnt stattdessen mit „bei diesem Zustand sieht der Bildschirm so aus“. Der mühsame Teil – das effiziente Vergleichen (Diffing) und Aktualisieren des DOM – wird zur Aufgabe von React.
Komponenten und JSX
Eine React-Komponente ist eine Funktion, deren Name mit einem Großbuchstaben beginnt und die JSX zurückgibt.
// Welcome.jsx
function Welcome({ name }) {
return <h1>Hello, {name}!</h1>;
}
export default function App() {
return (
<main>
<Welcome name="Ada" />
<Welcome name="Grace" />
</main>
);
}
JSX sieht aus wie HTML, ist aber JavaScript. Geschweifte Klammern betten Ausdrücke ein, und der Compiler übersetzt jedes Element in einen Funktionsaufruf. Da Komponenten lediglich Funktionen sind, können Sie diese komponieren, verschachteln und genauso wiederverwenden wie jede andere Funktion.
Props
Props sind die Inputs einer Komponente. Sie fließen abwärts vom Parent zum Child und sind schreibgeschützt – ein Child darf seine Props niemals mutieren.
// Card.jsx
function Card({ title, children, variant = "default" }) {
return (
<article className={`card card--${variant}`}>
<h3>{title}</h3>
{children}
</article>
);
}
Die children Prop umfasst alles, was du zwischen dem öffnenden und schließenden Tag verschachtelst, wodurch Komponenten zu composable Wrappern werden. Default-Werte ermöglichen es dem Aufrufer, Props wegzulassen, die in der Regel gleich bleiben.
State mit useState
Props sind für einen bestimmten Render-Vorgang fix. State sind Daten, die eine Komponente besitzt und die sich im Laufe der Zeit ändern können. Der useState Hook erstellt einen State-Wert und eine Setter-Funktion.
// Search.jsx
import { useState } from "react";
function Search({ onSearch }) {
const [query, setQuery] = useState("");
return (
<form onSubmit={(e) => { e.preventDefault(); onSearch(query); }}>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
</form>
);
}
Der Aufruf des Setters plant einen Re-Render mit dem neuen Wert. State-Updates erfolgen asynchron und gebündelt (batched). Zudem werden sie ersetzt und nicht zusammengeführt (merge), daher sollte beim Aktualisieren von strukturiertem State immer ein neues Objekt oder Array erstellt werden.
// update.jsx
setUser((prev) => ({ ...prev, name: "Grace" }));
setItems((prev) => [...prev, newItem]);
Die Verwendung der Updater-Funktion stellt sicher, dass der State korrekt aktualisiert wird, wenn der nächste Wert vom vorherigen abhängt.
Listen und Bedingungen rendern
React besitzt keine Template-Sprache für Schleifen und Verzweigungen – stattdessen nutzt man einfaches JavaScript.
// List.jsx
function List({ items }) {
if (items.length === 0) return <p>Nothing here yet.</p>;
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.label}</li>
))}
</ul>
);
}
Die key Prop ist für Listenelemente erforderlich. Weisen Sie ihr einen stabilen, eindeutigen Wert aus Ihren Daten zu, damit React die Elemente über verschiedene Render-Zyklen hinweg korrekt zuordnen kann. Der Ternary-Operator und && decken Inline-Bedingungen ab, wobei Early Returns oft besser lesbar sind.
Events
React-Events verwenden camelCase-Props und erhalten ein Synthetic-Event-Objekt.
// Button.jsx
<button
onClick={(event) => {
event.preventDefault();
submit();
}}
>
Save
</button>
Es ist wichtig, eine Funktion an die Prop zu übergeben – onClick={submit()} würde diese sofort während des Renderings aufrufen. Wenn ein Handler ein Argument benötigt, umschließen Sie ihn mit einer Arrow-Function.
Effects mit useEffect
useEffect synchronisiert deine Komponente mit Systemen außerhalb von React: Netzwerk-Requests, Subscriptions, Timer und direkte DOM-Manipulationen.
// Users.jsx
import { useEffect, useState } from "react";
function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
const controller = new AbortController();
fetch("/api/users", { signal: controller.signal })
.then((res) => res.json())
.then(setUsers)
.catch((err) => {
if (err.name !== "AbortError") console.error(err);
});
return () => controller.abort();
}, []);
return <ul>{users.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}
Das Dependency-Array steuert, wann der Effect erneut ausgeführt wird: Ein leeres Array führt den Effect einmal nach dem Mount aus, eine Liste von Werten führt ihn aus, sobald sich einer dieser Werte ändert, und ohne Array wird er nach jedem Render ausgeführt. Die zurückgegebene Funktion dient dem Cleanup, welcher vor dem nächsten Effect und beim Unmount ausgeführt wird. Bereinige stets Subscriptions und laufende Requests.
Context und Refs
Context teilt einen Wert mit einem gesamten Unterbaum, ohne dass Prop Drilling erforderlich ist.
// theme.jsx
const ThemeContext = createContext("light");
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Button() {
const theme = useContext(ThemeContext);
return <button className={theme}>Click</button>;
}
Refs speichern einen veränderbaren Wert oder einen DOM-Knoten, der keinen Re-Render auslösen soll.
// focus.jsx
const inputRef = useRef(null);
useEffect(() => inputRef.current?.focus(), []);
return <input ref={inputRef} />;
Datenfluss und State Lifting
Der Datenfluss in React erfolgt in nur eine Richtung. Wenn zwei Geschwister-Komponenten einen State teilen müssen, wird dieser in die nächstgelegene gemeinsame Elternkomponente gehoben (lifting state). Der Wert wird dann als Prop nach unten gereicht und die Setter-Funktion als Callback. Dies gewährleistet eine einzige Quelle der Wahrheit (single source of truth) und macht den Fluss der Änderungen leicht nachvollziehbar.
Bei tieferen Komponentenbäumen können Context oder eine dedizierte State-Library wie Zustand oder Redux den zusätzlichen Aufwand wert sein. Greifen Sie erst dann zu diesen Mitteln, wenn das Weiterreichen von Props zu mühsam wird, nicht schon vorher.
Hooks und Custom Hooks
Hooks sind Funktionen, mit denen Komponenten auf React-Features zugreifen können. Sie müssen auf der obersten Ebene aufgerufen werden, niemals innerhalb von Bedingungen oder Schleifen. Sobald du sie verstanden hast, kannst du gemeinsam genutzte Logik in einen Custom Hook auslagern.
// useFetch.js
function useFetch(url) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
let active = true;
fetch(url)
.then((res) => res.json())
.then((json) => active && setData(json))
.catch((err) => active && setError(err));
return () => { active = false; };
}, [url]);
return { data, error };
}
Ein Custom Hook ist einfach eine Funktion, deren Name mit use beginnt und die andere Hooks aufruft. Er teilt die Logik, nicht den State – jede Komponente, die ihn verwendet, erhält eine eigene, unabhängige Kopie.
Best Practices
- Halten Sie Komponenten klein und auf eine einzige Verantwortung fokussiert.
- Berechnen Sie abgeleitete Werte während des Renderings, anstatt sie im State zu speichern.
- Verwenden Sie die Updater-Form eines Setters, wenn der nächste Wert vom vorherigen abhängt.
- Geben Sie jedem Listenelement einen stabilen Key aus Ihren Daten.
- Räumen Sie Effects auf, insbesondere Subscriptions und ausstehende Requests.
- Heben Sie den State nur so weit an (Lift State), wie es unbedingt erforderlich ist.
- Extrahieren Sie wiederkehrende Logik in Custom Hooks.
- Greifen Sie auf Memoisation (
useMemo,useCallback,memo) erst zurück, nachdem Sie ein tatsächliches Problem gemessen haben.
Häufige Fehler
- Direkte Mutation des State anstatt die Erstellung eines neuen Wertes.
- Verwendung des Array-Index als Key, was das Reordering beeinträchtigt.
- Speichern von abgeleiteten Daten im State und deren Synchronisierung über einen Effect.
- Vergessen des Dependency-Arrays, was zu Endlosschleifen führt.
- Bedingter Aufruf eines Hooks, wodurch die Rules of Hooks verletzt werden.
- Optimierung mittels Memoisation, bevor tatsächlich ein Performance-Problem vorliegt.
Wie geht es weiter?
React ist die View-Layer; Next.js ergänzt dies um Routing, Data Fetching und Server Rendering. Vergleiche dieses Modell mit Vue und Svelte und vertiefe deine Grundlagen in JavaScript und TypeScript. Das interaktive React-Tutorial führt dich Schritt für Schritt durch dieselben Konzepte.