v19.2Latest

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

Diagramm mit einem Baum aus drei Komponenten. Die Elternkomponente enthält eine Blase, die einen Wert darstellt, der lila hervorgehoben ist. Der Wert fließt zu jedem der beiden Kinder hinab, die beide lila hervorgehoben sind.Diagramm mit einem Baum aus drei Komponenten. Die Elternkomponente enthält eine Blase, die einen Wert darstellt, der lila hervorgehoben ist. Der Wert fließt zu jedem der beiden Kinder hinab, die beide lila hervorgehoben sind.

Prop-Drilling

Diagramm mit einem Baum aus zehn Knoten, jeder Knoten mit zwei oder weniger Kindern. Der Wurzelknoten enthält eine Blase, die einen Wert darstellt, der lila hervorgehoben ist. Der Wert fließt durch die beiden Kinder hinab, die den Wert jeweils weitergeben, ihn aber nicht enthalten. Das linke Kind gibt den Wert an zwei Kinder weiter, die beide lila hervorgehoben sind. Das rechte Kind der Wurzel gibt den Wert an eines seiner beiden Kinder weiter – das rechte, das lila hervorgehoben ist. Dieses Kind gab den Wert durch sein einziges Kind weiter, das ihn an beide seiner beiden Kinder weiterreicht, die lila hervorgehoben sind.Diagramm mit einem Baum aus zehn Knoten, jeder Knoten mit zwei oder weniger Kindern. Der Wurzelknoten enthält eine Blase, die einen Wert darstellt, der lila hervorgehoben ist. Der Wert fließt durch die beiden Kinder hinab, die den Wert jeweils weitergeben, ihn aber nicht enthalten. Das linke Kind gibt den Wert an zwei Kinder weiter, die beide lila hervorgehoben sind. Das rechte Kind der Wurzel gibt den Wert an eines seiner beiden Kinder weiter – das rechte, das lila hervorgehoben ist. Dieses Kind gab den Wert durch sein einziges Kind weiter, das ihn an beide seiner beiden Kinder weiterreicht, die lila hervorgehoben sind.

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:

  1. Erstelleeinen Context. (Du kannst ihnLevelContextnennen, da er für die Überschriftenebene gedacht ist.)
  2. Verwendediesen Context in der Komponente, die die Daten benötigt. (HeadingwirdLevelContext)
  3. Stellediesen Context in der Komponente bereit, die die Daten angibt. (Section wird LevelContext)

Context ermöglicht es einem Elternteil – selbst einem entfernten! –, Daten für den gesamten Baum darin bereitzustellen.

Verwendung von Context in nahen Kindkomponenten

Diagramm mit einem Baum aus drei Komponenten. Das Elternteil enthält eine Blase, die einen Wert darstellt, der orange hervorgehoben ist und auf die beiden Kinder projiziert wird, die jeweils orange hervorgehoben sind.Diagramm mit einem Baum aus drei Komponenten. Das Elternteil enthält eine Blase, die einen Wert darstellt, der orange hervorgehoben ist und auf die beiden Kinder projiziert wird, die jeweils orange hervorgehoben sind.

Verwendung von Context in entfernten Kindkomponenten

Diagramm mit einem Baum aus zehn Knoten, jeder Knoten hat zwei oder weniger Kinder. Der Wurzel-Elternknoten enthält eine Blase, die einen Wert darstellt, der orange hervorgehoben ist. Der Wert wird direkt auf vier Blätter und eine Zwischenkomponente im Baum projiziert, die alle orange hervorgehoben sind. Keine der anderen Zwischenkomponenten ist hervorgehoben.Diagramm mit einem Baum aus zehn Knoten, jeder Knoten hat zwei oder weniger Kinder. Der Wurzel-Elternknoten enthält eine Blase, die einen Wert darstellt, der orange hervorgehoben ist. Der Wert wird direkt auf vier Blätter und eine Zwischenkomponente im Baum projiziert, die alle orange hervorgehoben sind. Keine der anderen Zwischenkomponenten ist hervorgehoben.

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:

  1. Sie übergeben einelevel-Prop an die<Section>.
  2. Sectionumschließt ihre Kinder mit<LevelContext value={level}>.
  3. 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:

  1. 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.
  2. 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 wiepostsan 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:
    1. Erstelle und exportiere ihn mitexport const MyContext = createContext(defaultValue).
    2. Übergib ihn an denuseContext(MyContext)Hook, um ihn in jeder Kindkomponente zu lesen, egal wie tief.
    3. Wickle Kinder in<MyContext value={...}>ein, um ihn von einem Elternteil bereitzustellen.
  • 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 alschildrenzu ü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.