Prop Drilling heißt, eine Prop durch Komponenten zu reichen, die sie nicht nutzen, nur damit eine Komponente weiter unten im Baum es kann. Hier wandert user von App über Page und Sidebar zu Avatar, der einzigen Komponente, die es liest.
Vier Ebenen, und nur die letzte interessiert sich für user. Die Konsole zeigt, wie Page und Sidebar für einen Wert laufen, den sie nie anzeigen. Klicke auf den Button, und sie loggen erneut.
Warum es schmerzt
Zwei oder drei Ebenen sind in Ordnung. Zum Problem wird Prop Drilling, wenn der Baum wächst:
- Die Komponenten in der Mitte tragen Props, die sie nicht nutzen.
PageundSidebarhaben jetzt eine Propuserin ihrer Signatur, also kannst du sie nicht lesen, ohne dich zu fragen, wofür sie ist. - Jede Änderung berührt jede Ebene. Füge
user.avatarUrloder einen zweiten Wert wieonLogouthinzu, und du bearbeitest jede Komponente auf dem Weg, nicht nur die beiden Enden. - Eine Komponente zu verschieben bricht die Kette. Verschiebe
Avatarin einen anderen Zweig, und du musstuserdurch einen neuen Weg fädeln. - Eine Prop geht leicht verloren. Vergiss, sie in einer Ebene weiterzugeben, und die tiefe Komponente bekommt
undefined, ohne Fehler an der Ebene, die sie verloren hat.
Probiere es: Füge in App eine Funktion onLogout hinzu, die Avatar aufrufen soll. Du wirst alle vier Komponenten bearbeiten.
Lösung 1: Komposition mit children
Diese Lösung wird oft übersehen. Statt Daten nach unten zu reichen, damit eine tiefe Komponente damit rendern kann, lass die Komponente, der die Daten gehören, die tiefe Komponente selbst rendern, und reich das fertige Element als children (oder als beliebige andere Prop) nach unten. Die Ebenen in der Mitte rendern einen Slot und sehen user nie.
Page und Sidebar wissen nicht mehr, dass es einen Nutzer gibt. App erzeugt Avatar dort, wo user lebt, und reicht es als fertiges Element nach unten. Solche Layout-Komponenten werden wiederverwendbar, und onLogout hinzuzufügen ist jetzt nur noch eine Änderung in App und Avatar. Beide loggen weiterhin bei jedem Klick, weil App ihnen bei jedem Render ein neues Element übergibt: Komposition vereinfacht den Code, nicht die Anzahl der Renders. Die Seite zu children behandelt weitere Slot-Muster.
Komposition funktioniert, wenn die Komponente, der die Daten gehören, auch entscheiden kann, wie der tiefe Teil aussieht. Sie hilft nicht, wenn die tiefe Komponente in einer Bibliothek oder einer Struktur steckt, die der Besitzer nicht rendert.
Lösung 2: Context
Wenn viele Komponenten in verschiedenen Tiefen denselben Wert brauchen (das Theme, den angemeldeten Nutzer, die aktuelle Sprache), leg ihn in Context. Jede Komponente unter dem Provider liest ihn mit useContext, und dazwischen ändert sich nichts.
Avatar und Greeting lesen den Nutzer aus zwei verschiedenen Tiefen, und Page und Sidebar nehmen keine Props. Füge irgendwo unter Page einen dritten Leser hinzu, und es funktioniert, ohne den Rest anzufassen. Der Preis: Der Datenfluss ist in den Props nicht mehr sichtbar, und jeder Leser rendert, wenn sich der Wert ändert. Die Seite zu useContext behandelt Provider, Standardwerte und diese Renders.
Lösung 3: eine State-Bibliothek
Für großen App-State, den viele Komponenten lesen und der sich oft ändert (ein Warenkorb, ein Dokumenteditor, Live-Daten), lässt eine State-Bibliothek wie Redux Toolkit, Zustand oder Jotai jede Komponente genau den Ausschnitt abonnieren, den sie nutzt. Das vermeidet sowohl das Drilling als auch die Kosten von „jeder Konsument rendert“ bei einem großen Context. Es ist aber eine weitere Abhängigkeit und ein weiterer Satz Konzepte, also greif dazu, wenn die ersten beiden Lösungen nicht mehr reichen, nicht um eine Prop zu reparieren, die drei Ebenen wandert.
// Zustand, for comparison (not available in the editor here)
const useCart = create((set) => ({
items: [],
add: (item) => set((s) => ({ items: [...s.items, item] })),
}));
function CartCount() {
const count = useCart((s) => s.items.length); // renders only when the count changes
return <span>{count}</span>;
}
Wann Prop Drilling in Ordnung ist
Props zu übergeben ist die Art, wie Daten in React fließen, und es ist das am leichtesten lesbare Muster: Du kannst einen Wert von oben bis unten verfolgen, indem du auf das JSX schaust. Eine Prop, die durch eine oder zwei Komponenten geht, die einen Teil davon nutzen, oder durch eine kurze Kette eng verwandter Komponenten, ist kein Problem, das gelöst werden muss. Verlagere State in das nächste gemeinsame Elternteil, reich ihn nach unten und wechsle den Ansatz erst, wenn die Ebenen in der Mitte anfangen, Props zu tragen, mit denen sie nichts anfangen können.
Häufig gestellte Fragen
Was ist Prop Drilling in React?
Eine Prop durch mehrere Ebenen von Komponenten nach unten zu reichen, die sie nicht nutzen, nur damit eine Komponente tief im Baum sie lesen kann. Jede Komponente in der Mitte nimmt die Prop an und gibt sie weiter.
Ist Prop Drilling schlecht?
Nicht an sich. Einen Wert zwei oder drei Ebenen nach unten zu reichen ist explizit und leicht nachzuvollziehen. Zum Problem wird es, wenn viele Ebenen Props tragen, die sie nie nutzen, sodass jede Umbenennung oder jedes neue Feld Dateien berührt, die nichts damit zu tun haben.
Wie vermeide ich Prop Drilling?
Probiere zuerst Komposition: Lass das Elternteil die tiefe Komponente bauen und als children oder als andere Prop nach unten reichen, damit die Ebenen in der Mitte die Daten nie sehen. Wenn viele Komponenten in verschiedenen Tiefen den Wert brauchen, nutze Context. Für großen State, der sich oft ändert, zieh eine State-Bibliothek in Betracht.
Ist Context die einzige Lösung für Prop Drilling?
Nein, und oft nicht die beste. Komposition mit children beseitigt das Drilling, ohne einen Context hinzuzufügen, und hält den Datenfluss im Code sichtbar.