React.memo vertieft
React.memo vertieft
~15 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
"React für Einsteiger" hat useMemo/useCallback eingeführt – Werte bzw. Funktionen zwischenspeichern. React.memo ist ihr Gegenstück auf Komponenten-Ebene: eine ganze Komponente wird nur dann neu gerendert, wenn sich ihre Props TATSÄCHLICH geändert haben.
Das Grundproblem: Eltern-Render zieht Kind-Render nach sich
Standardverhalten in React, OHNE React.memo: Rendert eine Elternkomponente neu, rendern ALLE ihre Kindkomponenten ebenfalls neu – UNABHÄNGIG davon, ob sich deren Props geändert haben. Das ist meist harmlos (Re-Rendern ist billig), kann aber bei teuren Berechnungen innerhalb einer Kindkomponente oder bei sehr vielen Kindkomponenten (siehe nächstes Kapitel: Listen-Virtualisierung) spürbar werden.
React.memo anwenden: ProductCard einpacken
ProductCard ist ein perfektes Beispiel: ProductListPage rendert bei JEDER Sucheingabe (query-State-Änderung) neu – und damit bisher auch JEDE einzelne ProductCard, selbst wenn deren eigene Props (name, price, ...) unverändert bleiben:
import { memo, useState } from 'react';
function ProductCard({ name, price, imageUrl, onSelect, onAddToCart }) {
const [isFavorite, setIsFavorite] = useState(false);
function handleFavoriteClick(event) {
event.stopPropagation();
setIsFavorite(!isFavorite);
}
function handleAddToCartClick(event) {
event.stopPropagation();
onAddToCart();
}
return (
<div className="product-card" onClick={onSelect}>
<img src={imageUrl} alt={name} className="product-card__image" />
<div className="product-card__info">
<h3 className="product-card__name">{name}</h3>
<p className="product-card__price">${price.toFixed(2)}</p>
</div>
<button className="product-card__favorite" onClick={handleFavoriteClick}>
{isFavorite ? '♥' : '♡'}
</button>
<button className="product-card__add-to-cart" onClick={handleAddToCartClick}>
In den Warenkorb
</button>
</div>
);
}
export default memo(ProductCard);Nur eine Änderung zählt: export default memo(ProductCard) statt export default ProductCard. Von jetzt an vergleicht React vor jedem Re-Render ALLE Props von ProductCard per flachem Vergleich (Object.is auf JEDEN einzelnen Prop-Wert) mit dem vorherigen Aufruf – sind sie IDENTISCH, wird der Render komplett übersprungen.
Die häufigste Falle: Inline-Funktionen als Props
Jetzt kommt der Haken, der React.memo für viele Einsteiger zur Enttäuschung macht: Sehen Sie sich ProductListPage.jsx an – dort wird onAddToCart={{() => dispatch(addItem(...))}} als Inline-Arrow-Function übergeben. Bei JEDEM Render von ProductListPage entsteht eine NEUE Funktion – auch wenn ihr Inhalt identisch aussieht, ist es für Object.is eine ANDERE Referenz. React.memo sieht also "onAddToCart hat sich geändert" und rendert trotzdem neu – memo allein hätte hier praktisch NICHTS gebracht.
Die Lösung: onAddToCart mit useCallback stabilisieren
Um React.memo wirksam zu machen, muss die übergebene Funktion selbst über Render-Durchläufe hinweg STABIL bleiben – genau das leistet useCallback aus "React für Einsteiger" Kapitel 10. Hier der relevante Ausschnitt aus ProductListPage.jsx, mit useCallback ergänzt (der Rest der Datei bleibt wie in Kapitel 29 gezeigt):
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
// ... übrige Imports unverändert
// ... innerhalb der Komponente, NACH dem useDispatch():
const handleAddToCart = useCallback(
(product) => dispatch(addItem({ sku: product.sku, name: product.name, price: product.price })),
[dispatch]
);
// ... im JSX:
<ProductCard
key={product.sku}
name={product.name}
price={product.price}
imageUrl={product.imageUrl}
onSelect={() => navigate(`/products/${product.sku}`)}
onAddToCart={() => handleAddToCart(product)}
/>Achtung: Aufgepasst: onAddToCart={{() => handleAddToCart(product)}} ist IMMER NOCH eine neue Inline-Funktion pro Render, weil sie product aus dem Closure einfängt! useCallback stabilisiert nur handleAddToCart SELBST (die Funktion, die einen product-Parameter entgegennimmt), nicht die pro-Produkt spezifische Inline-Version davon. Um DIESES Problem vollständig zu lösen, bräuchte ProductCard selbst sku/product als Prop und würde onAddToCart(product) intern mit dem eigenen sku aufrufen – eine Umstrukturierung, die den Rahmen dieses Kapitels sprengen würde. Der Kernpunkt hier: React.memo zu verstehen bedeutet auch zu verstehen, wie LEICHT man es versehentlich wirkungslos macht.
Wann sich React.memo überhaupt lohnt
- Die Komponente rendert TEUER (komplexe Berechnung, großer DOM-Baum, viele Kinder) UND
- sie bekommt HÄUFIG dieselben Props erneut (z. B. weil ihr Elternteil aus einem anderen Grund oft neu rendert) UND
- ihre Props sind tatsächlich REFERENZ-STABIL zu halten (primitives wie Zahlen/Strings sind das automatisch; Objekte/Arrays/Funktionen brauchen
useMemo/useCallback).
Achtung: React.memo ist NICHT kostenlos – der Props-Vergleich selbst kostet Zeit. Bei kleinen, billig zu rendernden Komponenten mit oft wechselnden Props kann React.memo die App sogar LANGSAMER machen (Vergleichskosten > eingesparte Renderkosten). Verwenden Sie den Profiler aus dem letzten Kapitel, um VOR und NACH dem Hinzufügen von memo zu messen, statt es blind überall einzusetzen.
Bonus: eine eigene Vergleichsfunktion
memo() akzeptiert optional ein zweites Argument – eine eigene Vergleichsfunktion, falls der Standard-Flachvergleich nicht passt (z. B. wenn ein Prop ein Array ist, dessen INHALT, nicht dessen Referenz, verglichen werden soll):
export default memo(ProductCard, (prevProps, nextProps) => {
// true zurückgeben = "gleich, NICHT neu rendern"
return prevProps.name === nextProps.name && prevProps.price === nextProps.price;
});Tipp: Faustregel: memo() ohne zweites Argument reicht für 95% der Fälle. Eine eigene Vergleichsfunktion ist ein Werkzeug für seltene Sonderfälle, nicht der Standardweg – sie ist selbst eine potenzielle Fehlerquelle (vergisst man einen Prop im Vergleich, kann ein echtes Update fälschlich übersprungen werden).