Tiefgreifendes Übergeben von Daten mit Context
Normalerweise übergeben Sie Informationen von einer übergeordneten Komponente an eine untergeordnete Komponente via Props. Das Übergeben von Props kann jedoch umständlich und unpraktisch werden, wenn Sie sie durch viele Zwischenkomponenten hindurchreichen müssen oder wenn viele Komponenten in Ihrer App dieselben Informationen benötigen.Contextermöglicht es der übergeordneten Komponente, bestimmte Informationen für jede Komponente im darunterliegenden Baum verfügbar zu machen – egal wie tief – ohne sie explizit über Props zu übergeben.
Sie werden lernen
- Was „Prop-Drilling“ ist
- Wie Sie wiederholtes Prop-Übergeben durch Context ersetzen
- Häufige Anwendungsfälle für Context
- Gängige Alternativen zu Context
Das Problem beim Übergeben von Props
Das Übergeben von Propsist eine hervorragende Möglichkeit, Daten explizit durch Ihre UI-Baumstruktur zu den Komponenten zu leiten, die sie verwenden.
Aber das Übergeben von Props kann umständlich und unpraktisch werden, wenn Sie eine Prop tief durch den Baum hindurchreichen müssen oder wenn viele Komponenten dieselbe Prop benötigen. Der nächstgelegene gemeinsame Vorfahre könnte weit von den Komponenten entfernt sein, die Daten benötigen, unddas Anheben des Statesso hoch kann zu einer Situation führen, die als „Prop-Drilling“ bezeichnet wird.
Anheben des States


Prop-Drilling


Wäre es nicht großartig, wenn es eine Möglichkeit gäbe, Daten zu den Komponenten im Baum zu „teleportieren“, die sie benötigen, ohne Props zu übergeben? Mit der Context-Funktion von React gibt es das!
Context: Eine Alternative zum Übergeben von Props
Context ermöglicht es einer übergeordneten Komponente, Daten für den gesamten darunterliegenden Baum bereitzustellen. Es gibt viele Anwendungsmöglichkeiten für Context. Hier ist ein Beispiel. Betrachten Sie dieseHeading-Komponente, die einelevelfür ihre Größe akzeptiert:
Nehmen wir an, Sie möchten, dass mehrere Überschriften innerhalb derselbenSectionimmer dieselbe Größe haben:
Derzeit übergibst du dielevel-Prop separat an jede<Heading>-Komponente:
Es wäre schön, wenn du dielevel-Prop an die<Section>-Komponente übergeben könntest und sie aus der<Heading>-Komponente entfernen könntest. Auf diese Weise könntest du sicherstellen, dass alle Überschriften im selben Abschnitt die gleiche Größe haben:
Aber wie kann die<Heading>-Komponente die Ebene ihrer nächstgelegenen<Section>-Komponente erfahren?Dafür bräuchte es eine Möglichkeit, dass eine Kindkomponente Daten von einer Stelle weiter oben im Baum „abfragen“ kann.
Das ist mit Props allein nicht möglich. Hier kommt Context ins Spiel. Du wirst es in drei Schritten umsetzen:
- Erstelleeinen Context. (Du kannst ihn
LevelContextnennen, da er für die Überschriftenebene gedacht ist.) - Verwendediesen Context in der Komponente, die die Daten benötigt. (
HeadingwirdLevelContext) - Stellediesen Context in der Komponente bereit, die die Daten angibt. (
SectionwirdLevelContext)
Context ermöglicht es einem Elternteil – selbst einem entfernten! –, Daten für den gesamten Baum darin bereitzustellen.
Verwendung von Context in nahen Kindkomponenten


Verwendung von Context in entfernten Kindkomponenten


Schritt 1: Erstelle den Context
Zuerst musst du den Context erstellen. Du musst ihnaus einer Datei exportieren, damit deine Komponenten ihn verwenden können:
Das einzige Argument fürcreateContextist derStandardwert. Hier steht1für die größte Überschriftenebene, aber Sie können jeden beliebigen Wert übergeben (sogar ein Objekt). Die Bedeutung des Standardwerts werden Sie im nächsten Schritt sehen.
Schritt 2: Den Kontext verwenden
Importieren Sie denuseContext-Hook aus React und Ihren Kontext:
Derzeit liest dieHeading-Komponentelevelaus den Props:
Entfernen Sie stattdessen dielevel-Prop und lesen Sie den Wert aus dem gerade importierten Kontext,LevelContext:
useContextist ein Hook. Genau wieuseStateunduseReducerkönnen Sie einen Hook nur direkt innerhalb einer React-Komponente aufrufen (nicht in Schleifen oder Bedingungen).useContextteilt React mit, dass dieHeading-Komponente denLevelContextlesen möchte.
Da dieHeading-Komponente jetzt keinelevel-Prop mehr hat, müssen Sie die level-Prop nicht mehr in Ihrem JSX anHeadingübergeben, wie hier:
Aktualisieren Sie das JSX so, dass stattdessen dieSection-Komponente sie erhält:
Zur Erinnerung, dies ist das Markup, das Sie zum Funktionieren bringen wollten:
Beachten Sie, dass dieses Beispiel noch nicht ganz funktioniert! Alle Überschriften haben die gleiche Größe, weilSie zwarden Kontext verwenden, ihn aber noch nichtbereitgestellthaben.React weiß nicht, woher es ihn bekommen soll!
Wenn Sie den Kontext nicht bereitstellen, verwendet React den Standardwert, den Sie im vorherigen Schritt angegeben haben. In diesem Beispiel haben Sie1als Argument fürcreateContextangegeben, also gibtuseContext(LevelContext) 1zurück, wodurch alle diese Überschriften zu<h1>werden. Lassen Sie uns dieses Problem beheben, indem jedeSectionihren eigenen Kontext bereitstellt.
Schritt 3: Den Kontext bereitstellen
DieSection-Komponente rendert derzeit ihre Kinder:
Umschließen Sie sie mit einem Kontext-Provider, um ihnen denLevelContextbereitzustellen:
Dies sagt React: „Wenn eine Komponente innerhalb dieser<Section>nachLevelContextfragt, gib ihr dieseslevel.“ Die Komponente verwendet den Wert des nächstgelegenen<LevelContext>im darüberliegenden UI-Baum.
Es ist das gleiche Ergebnis wie im ursprünglichen Code, aber Sie mussten dielevel-Prop nicht an jedeHeading-Komponente übergeben! Stattdessen „ermittelt“ sie ihre Überschriftenebene, indem sie die nächstgelegeneSectiondarüber fragt:
- Sie übergeben eine
level-Prop an die<Section>. Sectionumschließt ihre Kinder mit<LevelContext value={level}>.Headingfragt den nächstgelegenen Wert vonLevelContextdarüber mituseContext(LevelContext)ab.
Kontext in derselben Komponente verwenden und bereitstellen
Derzeit müssen Sie denleveljedes Abschnitts noch manuell angeben:
Da Kontext es Ihnen ermöglicht, Informationen von einer übergeordneten Komponente zu lesen, könnte jedeSection den levelvon der darüberliegendenSectionlesen und automatischlevel + 1nach unten weitergeben. So könnten Sie es machen:
Mit dieser Änderung müssen Sie dielevel-Propwederan die<Section>noch an die<Heading>übergeben:
Jetzt lesen sowohlHeadingals auchSectiondenLevelContext, um herauszufinden, wie "tief" sie sind. Und dieSectionumschließt ihre Kinder mit demLevelContext, um anzugeben, dass alles darin sich auf einer "tieferen" Ebene befindet.
Hinweis
Dieses Beispiel verwendet Überschriftenebenen, weil sie visuell zeigen, wie verschachtelte Komponenten Kontext überschreiben können. Aber Kontext ist auch für viele andere Anwendungsfälle nützlich. Sie können jede benötigte Information an den gesamten Unterbaum weitergeben: das aktuelle Farbschema, den aktuell angemeldeten Benutzer und so weiter.
Kontext durchläuft Zwischenkomponenten
Sie können beliebig viele Komponenten zwischen der Komponente, die den Kontext bereitstellt, und derjenigen, die ihn verwendet, einfügen. Dies umfasst sowohl eingebaute Komponenten wie<div>als auch Komponenten, die Sie selbst erstellen.
In diesem Beispiel wird dieselbePost-Komponente (mit einem gestrichelten Rahmen) auf zwei verschiedenen Verschachtelungsebenen gerendert. Beachten Sie, dass die<Heading>darin ihren Level automatisch von der nächstgelegenen<Section>erhält:
Sie mussten nichts Besonderes tun, damit dies funktioniert. EineSectionlegt den Kontext für den Baum darin fest, sodass Sie eine<Heading>überall einfügen können und sie die richtige Größe hat. Probieren Sie es in der Sandbox oben aus!
Kontext ermöglicht es Ihnen, Komponenten zu schreiben, die „sich ihrer Umgebung anpassen“ und sich je nachOrt(oder anders gesagt,in welchem Kontext) unterschiedlich darstellen.
Wie Kontext funktioniert, könnte Sie anCSS-Eigenschaftsvererbung erinnern.In CSS können Siecolor: bluefür ein<div>angeben, und jeder DOM-Knoten darin, egal wie tief, erbt diese Farbe, es sei denn, ein anderer DOM-Knoten dazwischen überschreibt sie mitcolor: green. Ähnlich ist es in React: Der einzige Weg, einen Kontext von oben zu überschreiben, besteht darin, die Kinder in einen Kontext-Provider mit einem anderen Wert zu wrappen.
In CSS überschreiben sich verschiedene Eigenschaften wiecolorundbackground-colornicht gegenseitig. Sie können die<div>-colorauf rot setzen, ohne diebackground-colorzu beeinflussen. Ähnlichüberschreiben sich verschiedene React-Kontexte nicht gegenseitig.Jeder Kontext, den Sie mitcreateContext()erstellen, ist völlig von anderen getrennt und verbindet Komponenten, diediesen speziellenKontext verwenden und bereitstellen. Eine Komponente kann problemlos viele verschiedene Kontexte verwenden oder bereitstellen.
Bevor Sie Kontext verwenden
Kontext ist sehr verlockend zu verwenden! Das bedeutet aber auch, dass es zu leicht ist, ihn zu übernutzen.Nur weil Sie einige Props mehrere Ebenen tief weitergeben müssen, heißt das nicht, dass Sie diese Informationen in den Kontext packen sollten.
Hier sind einige Alternativen, die Sie vor der Verwendung von Kontext in Betracht ziehen sollten:
- Beginnen Sie mit demWeitergeben von Props.Wenn Ihre Komponenten nicht trivial sind, ist es nicht ungewöhnlich, ein Dutzend Props durch ein Dutzend Komponenten zu reichen. Es mag sich wie eine Plackerei anfühlen, aber es macht sehr deutlich, welche Komponenten welche Daten verwenden! Die Person, die Ihren Code pflegt, wird Ihnen dankbar sein, dass Sie den Datenfluss mit Props explizit gemacht haben.
- Extrahieren Sie Komponenten undübergeben Sie JSX als Kinderan sie.Wenn Sie einige Daten durch viele Ebenen von Zwischenkomponenten reichen, die diese Daten nicht verwenden (und sie nur weiter nach unten reichen), bedeutet das oft, dass Sie vergessen haben, unterwegs einige Komponenten zu extrahieren. Zum Beispiel geben Sie vielleicht Daten-Props wie
postsan visuelle Komponenten weiter, die sie nicht direkt verwenden, wie<Layout posts={posts} />. Stattdessen sollteLayouteinechildren-Prop akzeptieren und<Layout><Posts posts={posts} /></Layout>rendern. Dies reduziert die Anzahl der Ebenen zwischen der Komponente, die die Daten angibt, und derjenigen, die sie benötigt.
Wenn keiner dieser Ansätze gut für Sie funktioniert, ziehen Sie Kontext in Betracht.
Anwendungsfälle für Kontext
- Theming:Wenn deine App dem Benutzer erlaubt, ihr Erscheinungsbild zu ändern (z.B. Dark Mode), kannst du einen Context-Provider an die Spitze deiner App setzen und diesen Context in Komponenten verwenden, die ihr visuelles Aussehen anpassen müssen.
- Aktueller Account:Viele Komponenten müssen möglicherweise den aktuell angemeldeten Benutzer kennen. Dies in einen Context zu setzen, macht es bequem, ihn überall im Baum zu lesen. Einige Apps erlauben es auch, mehrere Accounts gleichzeitig zu bedienen (z.B. um einen Kommentar als anderer Benutzer zu hinterlassen). In solchen Fällen kann es praktisch sein, einen Teil der UI in einen verschachtelten Provider mit einem anderen aktuellen Account-Wert zu packen.
- Routing:Die meisten Routing-Lösungen verwenden intern Context, um die aktuelle Route zu halten. So "weiß" jeder Link, ob er aktiv ist oder nicht. Wenn du deinen eigenen Router baust, möchtest du das vielleicht auch tun.
- State-Verwaltung:Wenn deine App wächst, könntest du am Ende viel State nahe der Spitze deiner App haben. Viele entfernte Komponenten darunter möchten ihn möglicherweise ändern. Es ist üblich,einen Reducer zusammen mit Context zu verwenden, um komplexen State zu verwalten und ihn ohne großen Aufwand an entfernte Komponenten weiterzugeben.
Context ist nicht auf statische Werte beschränkt. Wenn du beim nächsten Rendering einen anderen Wert übergibst, aktualisiert React alle darunter liegenden Komponenten, die ihn lesen! Deshalb wird Context oft in Kombination mit State verwendet.
Im Allgemeinen ist es ein gutes Zeichen, dass Context dir helfen wird, wenn einige Informationen von entfernten Komponenten in verschiedenen Teilen des Baums benötigt werden.
Zusammenfassung
- Context erlaubt es einer Komponente, einige Informationen an den gesamten Baum unter ihr bereitzustellen.
- Um Context zu übergeben:
- Erstelle und exportiere ihn mit
export const MyContext = createContext(defaultValue). - Übergib ihn an den
useContext(MyContext)Hook, um ihn in jeder Kindkomponente zu lesen, egal wie tief. - Wickle Kinder in
<MyContext value={...}>ein, um ihn von einem Elternteil bereitzustellen.
- Erstelle und exportiere ihn mit
- Context durchläuft alle Komponenten in der Mitte.
- Context ermöglicht es dir, Komponenten zu schreiben, die sich "an ihre Umgebung anpassen".
- Bevor du Context verwendest, versuche, Props zu übergeben oder JSX als
childrenzu übergeben.
Probieren Sie einige Herausforderungen aus
Challenge 1 of 1:Prop-Drilling durch Context ersetzen #
In diesem Beispiel ändert das Umschalten der Checkbox die imageSize-Prop, die an jede <PlaceImage>-Komponente übergeben wird. Der Zustand der Checkbox wird in der obersten App-Komponente gehalten, aber jede <PlaceImage>-Komponente muss ihn kennen.
Derzeit übergibt App die imageSize an List, die sie an jede Place-Komponente weitergibt, die sie dann an PlaceImage übergibt. Entfernen Sie das imageSize-Prop und übergeben Sie es stattdessen direkt von der App-Komponente an PlaceImage.
Sie können den Context in Context.js deklarieren.
