Erklärung zur React-Renderung: Zustandsaktualisierungen zu Bildschirmpixeln
Wie die Render-, Reconciliation- und Commit-Phasen von React mit dem Browser-Rendering verbunden sind: Layout, Paint und Composite, von einem Zustandsupdate bis zu den tatsächlichen Bildpunkten auf dem Bildschirm.
Quellen: Rendering Lifecycle in React: From State Update to Pixels on the Screen
Beim Arbeiten mit React hört man oft den Ausdruck „der Komponente wird neu gerendert“. Doch was bedeutet Rendering eigentlich im Hintergrund?
Dabei tauchen einige natürliche Fragen auf: Schickt React Updates an den DOM sobald sich der Zustand ändert? Zwingt eine Zustandsänderung jede einzelne Komponente dazu, neu gerendert zu werden? Und wenn React etwas neu rendernt, bedeutet das, dass der Browser sofort die ganze Seite neu zeichnet? Wie sich herausstellt, stimmt keines davon.
Um den Rendering-Lebenszyklus wirklich zu verstehen, muss man zwei voneinander unabhängige Systeme kennen, die zusammenarbeiten:
Reacts eigener Rendering-Lebenszyklus und der Rendering-Pipeline des Browsers.
Sobald man sieht, wie diese beiden Systeme miteinander verbunden sind, erscheinen Konzepte wie Vereinbarung, die Commit-Phase, useEffect, useLayoutEffect, Neuberechnung und Memoisierung nicht mehr wie Magie, sondern ergeben einen Sinn.
1. Das Gesamtbild
Auf höherer Ebene sieht der Ablauf so aus:
User Interaction
│
▼
State / Props Change
│
▼
┌──────────────────────┐
│ React Render │
│ │
│ Component executes │
│ New UI is returned │
└──────────┬───────────┘
│
▼
Reconciliation
│
▼
┌──────────────────────┐
│ Commit Phase │
│ │
│ Update real DOM │
└──────────┬───────────┘
│
▼
┌──────────────────────────┐
│ Browser Rendering │
│ │
│ Style → Layout → Paint │
│ → Composite │
└────────────┬─────────────┘
│
▼
Screen
Die wichtigste Erkenntnis ist folgende:
React bestimmt, wie die Benutzeroberfläche aussehen sollte und welche DOM-Änderungen notwendig sind. Der Browser wandelt anschließend diese DOM-Änderungen in tatsächliche Pixel auf dem Bildschirm um.
2. Was bedeutet „Rendering“?
Der Begriff Rendering wird in mehreren Bedeutungen verwendet.
React ruft Ihre Komponentenfunktion auf und bestimmt, wie die resultierende Benutzeroberfläche aussehen sollte.
Betrachten Sie diese Komponente:
function Counter() {
const [count, setCount] = useState(0);
return (
<button>
Count: {count}
</button>
);
}
Wenn React es rendernt, wird der Funktionskörper ausgeführt:
Counter()
↓
returns React element
↓
<button>Count: 0</button>
Das bedeutet nicht, dass der Browser die Schaltfläche bereits auf dem Bildschirm dargestellt hat.
Bevor irgendetwas Sichtbares erscheint, muss React erst entscheiden, ob sich der tatsächliche DOM überhaupt ändern muss.
Kurz gesagt:
React Render
≠
DOM Update
≠
Browser Paint
Rendering und Aktualisierung des DOMs sind zwei unterschiedliche Vorgänge.
3. Rendering-Lebenszyklus des Browsers
Bevor man sich mit den Interna von React beschäftigt, ist es hilfreich zu verstehen, was der Browser selbst tut.
Stellen Sie sich folgende Markup-Struktur vor:
<h1 class="title">
Hello World
</h1>
in Kombination mit dieser Stylesheet-Datei:
.title {
color: red;
font-size: 30px;
}
Bevor dieser Inhalt visuell angezeigt wird, durchläuft der Browser eine Reihe interner Schritte.
Eine vereinfachte Version dieses Ablaufs sieht so aus:
HTML
↓
DOM
↓
CSS
↓
CSSOM
↓
Render Tree
↓
Layout
↓
Paint
↓
Composite
↓
Screen
Lassen Sie uns jede Phase durchgehen.
4. HTML → DOM
Der Browser liest die HTML-Datei und erstellt das DOM, kurz für Document Object Model.
Angesichts dieser Markup-Struktur:
<body>
<h1>Hello World</h1>
<p>Welcome</p>
</body>
Der Browser erstellt eine Baumstruktur, die ungefähr so aussieht:
Document
│
└── body
├── h1
│ └── "Hello World"
│
└── p
└── "Welcome"
JavaScript kann anschließend diese Struktur direkt abfragen und verändern:
document.querySelector("h1");
Im Grunde beantwortet das DOM eine Frage:
Welche Elemente existieren auf der Seite?
5. CSS → CSSOM
Neben dem DOM analysiert der Browser auch Ihre CSS-Dateien und erstellt das CSSOM, also das CSS Object Model.
Zum Beispiel:
h1 {
color: red;
font-size: 30px;
}
Daraus ermittelt der Browser, welche Styles auf welche Elemente angewendet werden.
Konzeptionell sieht das so aus:
h1
├── color: red
└── font-size: 30px
Das CSSOM beantwortet eine andere Frage:
Wie sollten die einzelnen Elemente visuell gestaltet werden?
6. DOM + CSSOM → Render Tree
Danach verschmilzt der Browser das DOM mit dem CSSOM, um einen Render-Tree zu erzeugen.
Es gibt eine Feinheit, die erwähnenswert ist:
Nicht jeder Knoten im DOM wird schließlich im Render-Tree dargestellt.
Nehmen wir zum Beispiel diese Markup-Struktur:
<div class="hidden">
Hello
</div>
In Kombination mit dieser Regel:
.hidden {
display: none;
}
Das div-Element ist im DOM vorhanden:
DOM
└── div
Aber da es folgendes enthält:
display: none;
wird es vollständig aus dem visuellen Render-Tree ausgeklammert.
7. Layout
Sobald der Browser genau weiß, welche Elemente angezeigt werden müssen, berechnet er ihre Geometrie.
Dazu gehören die Berechnung von Dingen wie:
- Breite
- Höhe
- Position
- Ränder
- Padding
- Koordinaten
- Wie sich die Elemente räumlich zueinander verhalten
Nehmen wir diese Regel als Beispiel:
.box {
width: 200px;
height: 100px;
margin-left: 50px;
}
Der Browser muss konkrete Werte in dieser Form erzeugen:
x = 50px
y = ...
width = 200px
height = 100px
Dieser Berechnungsschritt wird als Layout bezeichnet und wird auch häufig als Reflow bezeichnet.
8. Paint
Sobald das Layout abgeschlossen ist, verfügt der Browser über Koordinaten für jedes sichtbare Element.
Der nächste Schritt besteht darin, auszumalen, wie diese Elemente tatsächlich aussehen.
Betrachten Sie folgende Styling-Einstellungen:
.box {
background: blue;
color: white;
border: 2px solid black;
}
Das Paint-Verfahren umfasst Dinge wie:
- Hintergrund
- Text
- Ränder
- Schatten
- Bilder
- Weitere visuelle Elemente
In dieser Reihenfolge:
Layout
↓
Paint
↓
Pixels
9. Composite
Heutige Browser können eine Seite in mehrere separate Schichten aufteilen.
Zum Beispiel:
Layer 1 → Page background
Layer 2 → Header
Layer 3 → Content
Layer 4 → Modal
Dann fügt der Kompositor des Browsers all diese Schichten zu dem einzigen Bild zusammen, das schließlich auf dem Bildschirm angezeigt wird.
Bestimmte CSS-Eigenschaften, wie zum Beispiel:
transform
opacity
Es kann in der Regel allein durch Kompositing animiert werden, wodurch die aufwändigere Layout-Arbeit vermieden wird, die Eigenschaften erfordern, die die Geometrie beeinflussen.
Deshalb ist das Animieren von transform in der Regel der bevorzugte Ansatz.
10. Reacts Rendering-Lebenszyklus
Nun, da die Browser-Seite erläutert wurde, schauen wir uns an, wie React das Rendering handhabt.
Ein nützliches mentales Modell teilt diesen Prozess in vier Phasen ein:
1. Trigger
2. Render
3. Reconciliation
4. Commit
5. Browser Rendering
Lassen Sie uns jede Phase einzeln durchgehen.
11. Phase 1 – Auslöser
Bevor irgendetwas anderes geschieht, benötigt React ein Signal, das folgendes signalisiert:
"Die Benutzeroberfläche könnte aktualisiert werden müssen."
Dieses Signal wird als Rendering-Auslöser bezeichnet.
Typische Auslöser sind:
1. Zustandsaktualisierung: setCount(10);
2. Änderung der Props: <Child count={count} />
Wenn ein Elternkomponente mit einem neuen Wert für count erneut gerendert wird, kann seine Kindkomponente dadurch neue Props erhalten.
3. Kontextaktualisierung: useContext(MyContext);
Jedes Mal, wenn sich der aus dem Kontext gelesene Wert ändert, müssen die Komponenten, die ihn verwenden, möglicherweise aktualisiert werden.
4. Aktualisierung externer Speicher: Bibliotheken und APIs können ebenfalls React-Updates auslösen.
Beispiele sind:
Redux
Zustand
useSyncExternalStore
Auch das Erstmalige Laden einer Anwendung zählt als Auslöser.
12. Phase 2 – Rendern
Sobald React eine Aktualisierung erkennt, wechselt es in die Render-Phase, in der es Komponenten ausführt, um das neue UI zu bestimmen.
Nehmen wir dieses Beispiel:
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
Zu Beginn beträgt count 0. React ruft Counter() auf und erzeugt:
<button>
Count: 0
</button>
Sobald auf die Schaltfläche geklickt wird und dies ausgeführt wird:
setCount(c => c + 1);
count wird auf 1 gesetzt, und React führt die Komponente erneut aus, wodurch Folgendes entsteht:
<button>
Count: 1
</button>
13. Wichtig: Rendern bedeutet nicht automatisch eine DOM-Änderung
Das ist ein entscheidender Unterschied im React-Modell.
Wenn React diese Funktion ausführt:
function Counter() {
console.log("Counter rendered");
return <button>Count: 1</button>;
}
wird lediglich berechnet, wie die Benutzeroberfläche sollte aussehen. Das bedeutet nicht automatisch, dass sich der DOM geändert hat oder dass der Browser etwas neu gemalt hat.
Die tatsächliche Abfolge sieht so aus:
Component executes
↓
React creates new element representation
↓
Reconciliation
↓
Determine required DOM changes
↓
Commit changes
Dies führt zu einer wichtigen Regel:
Das Rendern einer Komponente führt nicht automatisch zu einer DOM-Änderung.
14. Phase 3 – Abgleich
In dieser Phase vergleicht React die alte und neue Benutzeroberfläche, um genau herauszufinden, was sich geändert hat.
Nehmen wir an, der vorherige Baum sah so aus:
button
└── "Count: 0"
Und der neue Baum sieht so aus:
button
└── "Count: 1"
React kann herausfinden:
button → same
text → changed
Deshalb muss es nur den Teil des DOMs anfassen, der tatsächlich geändert wurde:
button.textContent = "Count: 1";
Der Hauptzweck besteht darin, den gesamten DOM-Baum nicht abzureißen und neu aufzubauen, wenn sich nur ein kleiner Teil geändert hat.
15. Schlüssel und Abgleich
Schlüssel sind besonders wichtig, wenn React mit Listen arbeitet.
Zum Beispiel:
items.map(item => (
<li key={item.id}>
{item.name}
</li>
))
React verlässt sich auf Schlüssel, um zu erkennen, welches Element bei jeder Neuzeichnung welches ist. Angenommen, man beginnt mit:
Before:
A
B
C
und kommt schließlich zu:
After:
A
C
Mit stabilen Schlüsseln kann React richtig ableiten:
A → same
B → removed
C → same
Das liefert React viel zuverlässigere Informationen für den Abgleich. Vermeiden Sie es, auf Array-Indizes als Schlüssel zu vertrauen, wenn die Listeneinträge umsortiert, hinzugefügt oder entfernt werden könnten.
16. Phase 4 – Commit
Sobald React herausgefunden hat, was geändert werden muss, geht es in die Commit-Phase, in der diese Änderungen tatsächlich im echten DOM angewendet werden.
Stellen Sie sich ein Button-Element vor, dessen Anzeige von einer Null auf eine Eins wechselt, sobald diese Phase abgeschlossen ist.
Zu diesem Zeitpunkt stimmen Reacts interner Baum und der aktuelle DOM endlich überein.
Die Commit-Phase ist ein separater Schritt von der Render-Phase:
Render Phase
↓
Calculate what should change
Commit Phase
↓
Actually apply DOM changes
17. Render-Phase gegen Commit-Phase
Das Verständnis dieser Trennung ist eines der häufigsten Themen, die bei Vorstellungsgesprächen untersucht werden.
Render-Phase
Während dieser Phase führt React folgende Schritte aus:
- Führt Komponentenfunktionen aus
- Berechnet die nächste Version der Benutzeroberfläche
- Führt Abgleichsprozesse durch
- Ermittelt, was geändert werden muss
Component
↓
React elements
↓
Reconciliation
Commit-Phase
Während dieser Phase führt React folgende Schritte aus:
- Führt die tatsächlichen DOM-Mutationen durch
- aktualisiert alle Referenzen
- führt die mit dieser Aktualisierung verbundenen Layout-Effekte aus
React result
↓
Real DOM
Hier ist eine vereinfachte Darstellung des Ablaufs:
React
│
▼
┌──────────────┐
│ Render Phase │
└──────┬───────┘
│
▼
┌───────────────┐
│ Commit Phase │
└──────┬────────┘
│
▼
Real DOM
18. Was passiert nach dem Commit?
Sobald die Commit-Phase abgeschlossen ist, übernimmt der Browser und stellt fest, dass sich das DOM geändert hat.
Je nach Art der Änderung muss der Browser möglicherweise mehrere Schritte durchlaufen:
DOM Changes
↓
Style Calculation
↓
Layout
↓
Paint
↓
Composite
Nicht jede Änderung erfordert alle diese Schritte, um sinnvolle Arbeit zu leisten – eine Änderung benötigt beispielsweise nur einen Neuanstrich, ohne dass eine vollständige Layout-Neuberechnung nötig ist.
Genau an diesem Punkt übergeben Reacts eigener Lebenszyklus den Prozess an den zuvor beschriebenen Rendering-Pipeline des Browsers.
19. useLayoutEffect
useLayoutEffect wird ausgelöst, nachdem React seine DOM-Änderungen vorgenommen hat, aber bevor der Browser die neuen Darstellungen zeichnen kann.
Die Abfolge sieht so aus:
React Render
↓
Commit
↓
DOM updated
↓
useLayoutEffect
↓
Browser Paint
function Tooltip() {
const ref = useRef(null);
useLayoutEffect(() => {
const rect = ref.current.getBoundingClientRect();
console.log(rect.width);
}, []);
return (
<div ref={ref}>
Tooltip
</div>
);
}
Dieser Hook ist das richtige Werkzeug, wenn Sie Folgendes benötigen:
- Elemente im DOM messen
- aktuelle Layout-Werte abrufen
- visuelle Anpassungen synchron vornehmen, bevor der Benutzer etwas sieht
Eine Vorsichtsmaßnahme: Wenn die Arbeit innerhalb von useLayoutEffect aufwendig ist, kann dies die Zeichnung verzögern und dazu führen, dass die Benutzeroberfläche träge wirkt.
20. useEffect
useEffect hingegen ist für Effekte gedacht, die nicht die Zeichnung des Browsers blockieren müssen.
useEffect(() => {
fetch("/api/products");
}, []);
Render
↓
Commit
↓
Browser Paint
↓
useEffect
Die tatsächlichen Planungsdetails sind etwas subtiler als das hier beschriebene, aber die wichtigste praktische Regel lautet:
Nutzen Sie
useLayoutEffectnur dann, wenn etwas vor dem Zeichnen durch den Browser geschehen muss; verwenden SieuseEffectfür alles, was auch nachdem die UI bereits sichtbar ist, funktionieren kann.
21. Vollständiges Beispiel
import { useEffect, useLayoutEffect, useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
console.log("Render");
useLayoutEffect(() => {
console.log("Layout Effect");
});
useEffect(() => {
console.log("Effect");
});
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
Konzeptionell sieht die Abfolge der Ereignisse so aus:
User clicks button
↓
setCount()
↓
State update scheduled
↓
Counter() executes
↓
"Render"
↓
React reconciles
↓
React commits DOM update
↓
"Layout Effect"
↓
Browser paints updated UI
↓
"Effect"
22. Verursacht eine Neuzusammenstellung des Elternteils eine Neuzusammenstellung des Kindes?
Nehmen Sie dieses Beispiel:
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
<Child />
</>
);
}
Wenn count aktualisiert wird:
Parent renders
↓
Child is encountered
↓
Child may render as part of reconciliation
So,
Parent state update
↓
Parent render
↓
Child can render
Dieses Verhalten ist einer der Gründe, warum React.memo als Optimierungstool existiert.
23. React.memo
Nehmen wir an, Sie haben:
const Child = React.memo(function Child() {
console.log("Child rendered");
return <p>Hello</p>;
});
Falls das Elternteil neu zusammengestellt wird, aber die an das Kind übergebenen Props referenziell unverändert bleiben, kann React die Neuzusammenstellung dieses gememorisierten Kindes vollständig überspringen.
So sieht die Idee visuell aus:
Parent update
↓
Parent renders
↓
React.memo checks Child props
↓
Props unchanged
↓
Skip Child render
Aber bedenken Sie Folgendes:
React.memobietet einen Leistungsbeschleuniger – es garantiert jedoch nicht, dass eine Komponente niemals erneut gerendert wird.
Änderungen im Kontext, der interne Zustand sowie andere Auslöser können die Aktualisierung trotz Memoisierung erzwingen.
24. useMemo und useCallback
Diese beiden Hooks werden häufig falsch verstanden.
useMemo: Es memoisiert einen berechneten Wert.
const result = useMemo(() => expensiveCalculation(data), [data]);
useCallback: Es memoisiert eine Funktionseintragung.
const handleClick = useCallback(() => {
doSomething(id);
}, [id]);
Sowohl Funktionen als auch Eintragungen sind in Situationen wirklich nützlich, in denen referenzielle Gleichheit dazu dient, wiederholte Arbeiten zu vermeiden – insbesondere beim Übergeben von Props an memoisierte Kindkomponenten.
Trotzdem führt das Standardmäßige Einsatz in jedem Codeabschnitt in der Regel zu mehr Komplexität, ohne echte Leistungsverbesserungen zu bringen.
25. Eine React-Renderung ist nicht dasselbe wie das Zeichnen im Browser
Dieser Punkt sollte ausdrücklich erwähnt werden.
Stellen Sie sich folgendes Komponentenbeispiel vor:
function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
<p>Hello</p>
</div>
);
}
Wenn sich count ändert:
App renders
↓
New React tree created
↓
Reconciliation
↓
Only count text differs
↓
Commit small DOM update
↓
Browser rendering
React baut diese Markup-Struktur nicht unbedingt von Grund auf neu auf:
<div>
<button>...</button>
<p>Hello</p>
</div>
26. Virtual DOM – Das richtige mentale Modell verstehen
Eine häufige Behauptung ist, dass React auf einem Virtual DOM setzt, um den Browser zu beschleunigen. Das ist eine Vereinfachung.
Was React tatsächlich tut, ist, eine interne Beschreibung der Benutzeroberfläche im Speicher zu halten und mithilfe von Reconciliation herauszufinden, was sich geändert hat.
Der Browser selbst kennt kein Konzept eines Virtual DOM. Er arbeitet nur mit Elementen wie HTML, dem DOM, CSS und letztendlich Pixeln.
Konzeptionell sieht der Ablauf so aus:
React Component
↓
React Element Tree
↓
Reconciliation
↓
DOM Mutations
↓
Browser
↓
Pixels
Der Virtuelle DOM ist niemals etwas, was der Browser tatsächlich darstellt.
27. Neudarstellung vs. Neuzeichnen
Es handelt sich um zwei unterschiedliche Konzepte, die nicht miteinander verwechselt werden sollten.
React-Neudarstellung
Dies tritt ein, wenn eine Komponentenfunktion erneut ausgeführt wird – im Grunde genommen wird Component() ausgerufen.
Browser-Neuzeichnen
Dies geschieht, wenn der Browser die Pixel auf dem Bildschirm neu zeichnet, weil sich eine visuelle Eigenschaft geändert hat – das ist der Paint-Schritt.
Denken Sie daher daran: eine React-Neudarstellung ist nicht gleichbedeutend mit einem Browser-Neuzeichnen.
Es ist durchaus möglich, dass eine Komponente erneut dargestellt wird, aber dennoch dieselbe DOM-Struktur liefert. Umgekehrt kann der Browser aus Gründen neu zeichnen, die nichts mit der Darstellung einer React-Komponente zu tun haben.
28. Umlayout vs. Neuzeichnen
Nehmen wir dieses Beispiel: element.style.width = „500px“;
Die Änderung der Breite eines Elements zwingt in der Regel zu einer erneuten Berechnung des Layouts:
Style
↓
Layout
↓
Paint
↓
Composite
Vergleichen wir das nun mit: element.style.backgroundColor = „red“;
Hier muss der Browser in der Regel keine geometrischen Berechnungen erneut durchführen:
Style
↓
Paint
↓
Composite
Bei bestimmten Änderungen der Transformation oder Transparenz muss der Browser möglicherweise nur:
Composite
Es ist sehr wichtig, diesen Unterschied zu verstehen, wenn man Animationen oder Benutzeroberflächen mit hohen Renderanforderungen optimiert.
29. Anwendung in einer echten React-Situation
Stellen Sie sich eine typische E-Commerce-Produktseite vor, die wie folgt strukturiert ist:
Product Page
├── Navbar
├── Product Gallery
├── Product Information
├── Add To Cart
├── Reviews
└── Recommendations
Nehmen wir an, ein Benutzer klickt auf die Schaltfläche Zum Warenkorb hinzufügen, wodurch die Anzahl im Warenkorb aktualisiert wird.
Konzeptionell könnte die Abfolge der Ereignisse so aussehen:
User clicks "Add to Cart"
↓
Cart state updates
↓
React schedules update
↓
Relevant components render
↓
React reconciles new tree
↓
Only required DOM changes are committed
↓
Browser performs necessary rendering work
↓
Updated cart count appears
In einer gut strukturierten Anwendung sollte diese Aktualisierung der Warenkorbanzahl nicht unnötigerweise dazu führen, dass die gesamte Benutzeroberfläche neu erstellt wird. Genau deshalb sind Entscheidungen bezüglich der Grenzen der Komponenten, des Ortes, an dem der Zustand gespeichert wird, der Memoisierung sowie der Gesamtrendering-Architektur von großer Bedeutung.
30. Wenn teures Rendering zu einem Leistungsproblem wird
function ProductList({ products }) {
return products.map(product => (
<ExpensiveProductCard
key={product.id}
product={product}
/>
));
}
Nehmen wir an, diese Liste zeigt 10.000 Produkte an, und eine nicht verwandte Zustandsänderung veranlasst unnötige Arbeiten in einer großen Anzahl von Komponenten – die Leistung kann deutlich nachlassen.
Häufige Abmilderungstechniken sind:
React.memo
+
Stable props
+
useMemo/useCallback where justified
+
Virtualization
+
Pagination
+
Code splitting
Bei besonders großen Listen ist Virtualisierung in der Regel besonders wirksam:
10000 items
↓
Only visible ~20-50 items rendered
31. Wie Rendering mit koncurrentem React zusammenwirkt
Neuere Versionen von React bieten fortgeschrittenere Planungsfunktionen. React kann verschiedenen Aktualisierungen Prioritäten zuweisen und bei Bedarf die Render-Arbeit pausieren oder verschieben.
Das wichtigste Denkmuster, das man beibehalten sollte, ist:
Render work
↓
Can be scheduled
↓
Can be interrupted in modern React
↓
Eventually committed
Genau deshalb sollten Nebeneffekte nicht innerhalb der Render-Logik einer Komponente liegen:
function Component() {
fetch("/api/data"); // ❌ Side effect during render
return <div>Hello</div>;
}
Stattdessen sollte man einen Effect-Hook oder ein geeignetes Muster zur Datenerfassung verwenden. Idealerweise sollte die Render-Phase rein bleiben.
32. Der Vorteil einer reinen Renderung
Konzeptionell sollte sich eine React-Komponente wie eine reine Funktion verhalten:
UI = f(props, state)
Bei denselben Props und State sollte sie immer dieselbe Benutzeroberfläche darstellen. Zum Beispiel:
function Greeting({ name }) {
return <h1>Hello {name}</h1>;
}
Dieses Verhalten ist vorhersehbar und sicher.
Dagegen ist es problematisch, etwas Derartiges innerhalb des Renders zu tun:
function Component() {
localStorage.setItem("x", "1"); // ❌
fetch("/api"); // ❌
return <div>Hello</div>;
}
Das führt zu Nebeneffekten in einer Phase, die von React möglicherweise mehrmals ausgeführt oder auf unerwartete Weise geplant werden kann.
Nebeneffekte gehören an einen anderen Ort – innerhalb von Effects, Event-Handlern oder den Datenabrufmechanismen, die Ihr Framework bereitstellt.
33. Rendering-Lebenszyklus – Vollständiges Denkmodell
USER ACTION
│
▼
State / Props Update
│
▼
React Schedules Work
│
▼
┌──────────────┐
│ RENDER │
│ │
│ Components │
│ execute │
└──────┬───────┘
│
▼
Reconciliation
│
▼
┌──────────────┐
│ COMMIT │
│ │
│ DOM mutation │
└──────┬───────┘
│
▼
useLayoutEffect
│
▼
Browser Rendering
│
▼
Style Calculation
│
▼
Layout
│
▼
Paint
│
▼
Composite
│
▼
SCREEN
│
▼
useEffect
Übliche Vorstellungsgesprächsfragen
Q1. Aktualisiert eine Zustandsänderung sofort den DOM?
Nicht unbedingt. Eine Zustandsänderung plant lediglich die Arbeit für React an. React rendernt das Komponente, vergleicht die Ausgabe mit der vorherigen und führt erst dann die tatsächlich notwendigen DOM-Änderungen durch.
setState
↓
Render
↓
Reconciliation
↓
Commit
↓
DOM
Q2. Führt jedes Render zu einer DOM-Änderung?
Nicht immer. React kann ein Komponente rendern und dennoch feststellen, dass die resultierende UI-Beschreibung identisch mit dem Ist-Zustand auf dem Bildschirm ist; in diesem Fall wird der DOM unberührt gelassen.
Render
↓
Compare
↓
No meaningful DOM difference
↓
No DOM mutation
Q3. Zwingt das Neuerstellen der Darstellung in React den Browser dazu, neu zu zeichnen?
Nicht unbedingt. Die Darstellung in React und das Zeichnen im Browser sind zwei separate Prozesse.
React Render ≠ Browser Paint
Q4. Was bedeutet Reconciliation?
Reconciliation ist der Prozess, bei dem React den neu berechneten UI- Baum mit dem vorherigen vergleicht, um genau herauszufinden, was geändert werden muss.
Q5. Was passiert in der Commit-Phase?
Während der Commit-Phase wendet React die erforderlichen Änderungen im Host-Umfeld an – beispielsweise indem es die tatsächlichen DOM-Node im Browser aktualisiert.
Q6. Inwiefern unterscheiden sich useEffect und useLayoutEffect?
Eine einfache Erinnerungshilfe:
useLayoutEffect
→ after DOM commit, before paint
useEffect
→ after commit and generally after paint
Wenden Sie useLayoutEffect an, wenn Sie das Layout synchron messen oder anpassen müssen, bevor der Benutzer den neuen Frame sieht. Verwenden Sie useEffect für die meisten Nebeneffekte, bei denen kein Blockieren der Darstellung erforderlich ist.
Q7. Was löst die Erneutdarstellung einer Komponente aus?
Typische Auslöser sind: eine anfängliche Initialisierung, ein Zustandsupdate, eine erneute Darstellung eines Elternteils, eine Änderung der Props, ein Update des Contexts oder ein Update aus einem externen Speicher.
Q8. Wie kann die Darstellung optimiert werden? [IMP]
Anstatt standardmäßig überall useMemo zu verwenden, ermitteln Sie zunächst genau, wo das eigentliche Engpass ist. Betrachten Sie anschließend diese Ansätze:
1. Halten Sie den Zustand in der Nähe dessen, wo er verwendet wird
Vermeiden Sie eine Struktur, in der sich der Zustand an der Spitze befindet und alle Nachkommen davon abhängen:
App
↓
Everything depends on state
prefer:
Feature
↓
Local state
Es ist besser, einen engeren Platz einzunehmen, wenn der globale Zustand tatsächlich nicht notwendig ist.
2. Teure Komponenten selektiv memorisieren
const ProductCard = React.memo(ProductCardComponent);
3. Achten Sie auf Objekterstellung, die die Memorisierung stört
Anstatt immer wieder ein neues Objekt direkt hier einzufügen:
<Child config={{ theme: "dark" }} />
Man benötigt möglicherweise eine stabile Referenz, wenn Child in React.memo eingebettet ist und die Identität wichtig ist – aber beheben Sie das erst, wenn es tatsächlich ein Problem darstellt.
4. Lange Listen virtualisieren
Anstatt alle 10.000 DOM-Elemente auf einmal anzuzeigen, zeigen Sie nur die derzeit sichtbaren an.
5. Das Laden aufwändiger Funktionen verzögern
const AdminDashboard = lazy(() => import("./AdminDashboard"));
Dadurch verringert sich die Menge an JavaScript, die im Voraus heruntergeladen und ausgeführt werden muss.
6. Browserfreundliche Rendering-Techniken bevorzugen
Bewegen Sie Elemente möglichst mit günstigen Eigenschaften wie Transformations- und Transparenz-Einstellungen, und vermeiden Sie teure Neuberechnungen des Layouts in jedem Frame.