Eine Einführung in die Kontext-API von React

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ In diesem Artikel erfahren Sie, wie Sie die Kontext-API von React verwenden, mit der Sie globale Anwendungszustände in Ihren React-Apps verwalten können, ohne auf Props Drilling zurückgreifen zu müssen.

Für dieses Tutorial sollten Sie ein gutes Verständnis von Hooks haben. Bevor wir jedoch beginnen, werde ich kurz erläutern, was sie sind und welche Hooks wir in diesem Artikel verwenden werden.

Laut React Docs:

Hooks sind eine neue Ergänzung in React 16.8. Mit ihnen können Sie Status- und andere React-Funktionen verwenden, ohne eine Klasse zu schreiben.“

Das ist im Grunde das, was ein React-Hook ist. Es ermöglicht uns, State, Refs und andere React-Funktionen in unseren funktionalen Komponenten zu verwenden.

Lassen Sie uns die beiden Haken besprechen, denen wir in diesem Artikel begegnen werden.

Der useState Hook

Der useState-Hook ermöglicht es uns, den Zustand in unseren funktionalen Komponenten zu verwenden . Ein useState Hook nimmt den Anfangswert unseres Zustands als einziges Argument und gibt ein Array aus zwei Elementen zurück. Das erste Element ist unsere Zustandsvariable und das zweite Element ist eine Funktion, in der wir den Wert der Zustandsvariablen aktualisieren können.

Schauen wir uns das folgende Beispiel an:

 import React, {useState} from "react"; function SampleComponent(){ const [count, setCount] = useState(0); }

Hier ist count unsere Zustandsvariable und ihr Anfangswert ist 0 , während setCount eine Funktion ist, mit der wir den Wert von count aktualisieren können.

Der useContext Hook

Ich werde dies später in diesem Artikel besprechen, aber dieser Hook ermöglicht es uns im Grunde, den Wert eines Kontexts zu konsumieren . Was das eigentlich bedeutet, wird später in diesem Artikel deutlicher.

Garn-Arbeitsbereiche

Mit Yarn-Arbeitsbereichen können Sie Ihre Projektcodebasis mithilfe eines monolithischen Repositorys (Monorepo) organisieren. React ist ein gutes Beispiel für ein Open-Source-Projekt, das Monorepo ist und Yarn-Arbeitsbereiche verwendet, um diesen Zweck zu erreichen. Lesen Sie einen verwandten Artikel →

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Warum brauchen wir die Kontext-API?

Wir wollen eine „Theme Toggler“-Komponente bauen, die für unsere React-App zwischen Hell- und Dunkelmodus umschaltet. Jede Komponente muss Zugriff auf den aktuellen Themenmodus haben, damit sie entsprechend gestaltet werden kann.

Normalerweise würden wir den aktuellen Designmodus allen Komponenten über Requisiten zur Verfügung stellen und das aktuelle Design mit state aktualisieren:

 import React from "react"; import ReactDOM from "react-dom"; function App() { return ( <div> <Text theme= "blue" /> <h1>{theme}</h1> </div> ); } function Text({theme}) { return( <h1 style = {{ color: `${theme}` }}>{theme}</h1> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);

Im obigen Codebeispiel haben wir eine Textkomponente erstellt, die ein h1 -Element rendert. Die Farbe des h1 -Elements hängt vom aktuellen Designmodus ab. Derzeit ist das Thema blau. Wir können zwischen blue und red Themen umschalten, indem wir state verwenden.

Wir werden mit dem useState Hook einen Zustand namens „theme“ erstellen. Der Hook useState gibt den aktuellen Wert des Designs und eine Funktion zurück, mit der wir das Design aktualisieren können.

Lassen Sie uns also unseren Themenstatus erstellen:

 const [theme, setTheme] = React.useState("blue");

Wir werden unserer App Komponente auch ein Schaltflächenelement hinzufügen. Diese Schaltfläche wird zum Umschalten der Themen verwendet und benötigt einen Click-Event-Handler. Lassen Sie uns also den Click-Event-Handler wie folgt schreiben:

 const onClickHandler = () => { setTheme(); }

Jetzt wollen wir das neue Thema auf Red setzen, wenn das aktuelle Thema Blue ist, und umgekehrt. Anstatt eine if -Anweisung zu verwenden, geht dies bequemer mit Hilfe des ternären Operators in JavaScript.

 setTheme( theme === "red"? "blue": "red");

Jetzt haben wir also unseren onClick Handler geschrieben. Lassen Sie uns dieses Schaltflächenelement zur App -Komponente hinzufügen:

 <button onClick = {onClickHandler}>Change theme</button>

Lassen Sie uns auch den Wert der Themen-Requisiten der Text-Komponente in den Themenzustand ändern.

 <Text theme={theme}/>

Jetzt sollten wir das haben:

 import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { const[theme, setTheme] = React.useState("red"); const onClickHandler = () => { setTheme( theme === "red"? "blue": "red"); } return ( <div> <Text theme={theme}/> <button onClick = {onClickHandler}>Change theme</button> </div> ); } function Text({theme}) { return( <h1 style = {{ color: `${theme}` }}>{theme}</h1> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);

Wir können jetzt zwischen unseren beiden Themen wechseln. Wenn dies jedoch eine viel größere Anwendung wäre, wäre es schwierig, das Thema in tief verschachtelten Komponenten zu verwenden, und der Code wird unhandlich.

Einführung in die Kontext-API

Lassen Sie mich die Kontext-API vorstellen. Laut der React-Dokumentation:

„Kontext bietet eine Möglichkeit, Daten durch den Komponentenbaum zu leiten, ohne Requisiten auf jeder Ebene manuell weitergeben zu müssen.“

Für eine tiefergehende Definition bietet es Ihnen die Möglichkeit, bestimmte Daten für alle Komponenten im gesamten Komponentenbaum verfügbar zu machen, unabhängig davon, wie tief diese Komponente verschachtelt ist.

Schauen wir uns dieses Beispiel an:

 const App = () => { return( <ParentComponent theme = "light"/> ); } const ParentComponent = (props) => ( <Child theme = {props.theme} /> ) const Child = (props) => ( <Grandchild theme = {props.theme} /> ) const Grandchild = (props) => ( <p>Theme: {props.theme}</p> )

Im obigen Beispiel haben wir das Anwendungsthema mit einer Requisite in der ParentComponent namens theme angegeben. Wir mussten diese Requisiten an alle Komponenten im Komponentenbaum weitergeben, um sie dorthin zu bringen, wo sie benötigt werden, nämlich die GrandChild Komponente. Die ChildComponent hatte nichts mit den Theme-Requisiten zu tun, sondern diente lediglich als Vermittler.

Stellen Sie sich nun vor, die GrandChild Komponente wäre tiefer verschachtelt als im oberen Beispiel. Wir müssten die Themenrequisiten genauso weitergeben wie hier, was umständlich wäre. Dieses Problem löst Context . Mit Context hat jede Komponente im Komponentenbaum Zugriff auf alle Daten, die wir in unseren Kontext einfügen.

Beginnen wir mit dem Context

Es ist an der Zeit, die Schaltfläche zum Umschalten des Themas, die wir am Anfang des Artikels erstellt haben, mit der Kontext-API zu replizieren. Diesmal wird unser Themenumschalter eine separate Komponente sein. Wir werden eine ThemeToggler Komponente erstellen, die das Design unserer React-App mithilfe von Context umschaltet.

Lassen Sie uns zunächst unsere React-App initialisieren. (Ich bevorzuge die Verwendung von create-react-app , aber Sie können jede Methode verwenden, die Sie bevorzugen.)

Nachdem Sie Ihr React-Projekt initialisiert haben, erstellen Sie eine Datei namens ThemeContext.js in Ihrem /src -Ordner. Sie können auch einen Ordner namens /context erstellen und Ihre ThemeContext -Datei dort ablegen, wenn Sie möchten.

Lassen Sie uns nun weitermachen.

Erstellen Ihrer Kontext-API

Wir erstellen unseren Designkontext in unserer ThemeContext.js -Datei.

Um einen Kontext zu erstellen, verwenden wir React.createContext , das ein Kontextobjekt erstellt. Sie können alles als Argument an React.createContext . In diesem Fall übergeben wir eine Zeichenfolge, die den aktuellen Themenmodus darstellt. Unser aktueller Themenmodus ist also der „leichte“ Themenmodus.

 import React from "react"; const ThemeContext = React.createContext("light"); export default ThemeContext;

Um diesen Kontext allen unseren React-Komponenten zur Verfügung zu stellen, müssen wir einen Provider verwenden. Was ist ein Anbieter? Laut der React-Dokumentation enthält jedes Kontextobjekt eine Provider-React-Komponente , die es konsumierenden Komponenten ermöglicht, Kontextänderungen zu abonnieren. Es ist der Anbieter, der es ermöglicht, dass der Kontext von anderen Komponenten verwendet wird. Lassen Sie uns dennoch unseren Anbieter erstellen.

Gehen Sie zu Ihrer App.js -Datei. Um unseren Provider zu erstellen, müssen wir unseren ThemeContext importieren.

Nachdem der ThemeContext importiert wurde, müssen wir den Inhalt unserer App -Komponente in ThemeContext.Provider Tags einschließen und der ThemeContext.Provider Komponente eine Requisite namens value zuweisen, die die Daten enthält, die wir unserem Komponentenbaum zur Verfügung stellen möchten.

 function App() { const theme = "light"; return ( <ThemeContext.Provider value = {theme}> <div> </div> </ThemeContext.Provider> ); }

Jetzt steht also allen unseren Komponenten der Wert „Licht“ zur Verfügung (was wir bald schreiben werden).

Erstellen unserer Designdatei

Jetzt erstellen wir unsere Themendatei, die die unterschiedlichen Farbwerte für unsere hellen und dunklen Themen enthält. Erstellen Sie in Ihrem Ordner /src eine Datei mit dem Namen Colors.js .

In Colors.js erstellen wir ein Objekt namens AppTheme . Dieses Objekt enthält die Farben für unsere Themen. Wenn Sie fertig sind, exportieren Sie das AppTheme Objekt wie folgt:

 const AppTheme = { light: { textColor: "#000", backgroundColor: "#fff" }, dark: { textColor: "#fff", backgroundColor: "#333" } } export default AppTheme;

Jetzt ist es an der Zeit, mit der Erstellung unserer verschiedenen React-Komponenten zu beginnen.

Erstellen unserer React-Komponenten

Lassen Sie uns die folgenden Komponenten erstellen:

  • Header
  • ThemeToggler
  • MainWithClass

Header.jsx

 import React from "react"; import ThemeToggler from "./ThemeToggler"; const headerStyles = { padding: "1rem", display: "flex", justifyContent: "space-between", alignItems: "center" } const Header = () => { return( <header style = {headerStyles}> <h1>Context API</h1> <ThemeToggler /> </header> ); } export default Header;

ThemeToggler.jsx

(Im Moment geben wir nur ein leeres div zurück.)

 import React from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { return( <div style = {themeTogglerStyle}> </div> ); } export default ThemeToggler;

Konsumieren von Kontext mit klassenbasierten Komponenten

Hier verwenden wir den Wert unseres ThemeContext . Wie Sie vielleicht bereits wissen, haben wir zwei Methoden, um Komponenten in React zu schreiben : durch Funktionen oder Klassen. Der Prozess des Verwendungskontexts in beiden Methoden ist unterschiedlich, daher erstellen wir zwei Komponenten, die als Hauptabschnitt unserer Anwendung dienen: MainWithClass und MainWithFunction .

Beginnen wir mit MainWithClass .

MainWithClass.jsx

Wir müssen unseren ThemeContext und unser AppTheme . Sobald dies erledigt ist, schreiben wir eine Klasse, die unser JSX von einer Rendermethode zurückgibt. Jetzt müssen wir unseren Kontext konsumieren. Es gibt zwei Methoden, dies mit klassenbasierten Komponenten zu tun:

  1. Die erste Methode ist durch Class.contextType .

    Um diese Methode zu verwenden, weisen wir das Context-Objekt aus unserem ThemeContext der Eigenschaft contextType unserer Klasse zu. Danach können wir mit this.context auf den Kontextwert zugreifen. Sie können dies auch in einer der Lebenszyklusmethoden und sogar in der Rendermethode referenzieren.

     import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component{ constructor(){ super(); } static contextType = ThemeContext; render(){ const currentTheme = AppTheme[this.context]; return( <main></main> ); } }

    Nachdem ThemeContext der Eigenschaft contextType unserer Klasse zugewiesen hatte, habe ich das aktuelle Designobjekt in der Variablen currentTheme .

    Jetzt holen wir uns die Farben aus der Variablen currentTheme und verwenden sie, um ein Markup zu stylen.
     render() { const currentTheme = AppTheme[this.context]; return ( <main style={{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main>

    Das ist es! Diese Methode beschränkt Sie jedoch darauf, nur einen Kontext zu verwenden.
  2. Die zweite Methode ist ThemeContext.Consumer , die die Verwendung eines Verbrauchers beinhaltet. Jedes Kontextobjekt enthält auch eine Consumer React-Komponente, die in einer klassenbasierten Komponente verwendet werden kann. Die Consumer-Komponente nimmt ein untergeordnetes Element als Funktion und diese Funktion gibt einen React-Knoten zurück. Der aktuelle Kontextwert wird dieser Funktion als Argument übergeben.

    Lassen Sie uns nun den Code in unserer MainWithClass Komponente durch diesen ersetzen:
     class Main extends Component { constructor() { super(); this.state = { } } render(){ return( <ThemeContext.Consumer> { (theme) => { const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ) } } </ThemeContext.Consumer> ); } }

    Wie Sie sehen können, haben wir den aktuellen Wert unseres ThemeContext verwendet, den wir als „theme“ aliasiert haben, und wir haben die Farbwerte für diesen Themenmodus abgerufen und sie der Variablen currentTheme . Mit dieser Methode können Sie mehrere Verbraucher verwenden.

Dies sind die beiden Methoden zum Konsumieren von Kontext mit klassenbasierten Komponenten.

Konsumieren von Kontext mit funktionalen Komponenten

Das Konsumieren von Kontext mit funktionalen Komponenten ist einfacher und weniger mühsam als mit klassenbasierten Komponenten. Um den Kontext in einer funktionalen Komponente zu nutzen, verwenden wir einen Hook namens useContext .

So würde die Verwendung unseres ThemeContext mit einer funktionalen Komponente aussehen:

 const Main = () => { const theme = useContext(ThemeContext); const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } export default Main;

Wie Sie sehen, mussten wir lediglich unseren useContext Hook mit unserem als Argument übergebenen ThemeContext verwenden.

Hinweis : Sie müssen diese verschiedenen Komponenten in der App.js-Datei verwenden, um die Ergebnisse zu sehen.

Aktualisieren unseres Designs mit der ThemeToggler Komponente

Jetzt werden wir an unserer ThemeToggler Komponente arbeiten. Wir müssen in der Lage sein, zwischen den hellen und dunklen Themen zu wechseln. Dazu müssen wir unsere ThemeContext.js bearbeiten. Unser React.createContext nimmt nun ein Objekt, das dem Ergebnis eines useState -Hooks ähnelt, als Argument.

 const ThemeContext = React.createContext(["light", () => {}]);

Wir haben ein Array an die Funktion React.createContext . Das erste Element im Array ist der aktuelle Designmodus und das zweite Element ist die Funktion, die zum Aktualisieren des Designs verwendet wird. Wie ich schon sagte, ähnelt dies nur dem Ergebnis eines useState , aber es ist nicht genau das Ergebnis eines useState .

Jetzt werden wir unsere App.js -Datei bearbeiten. Wir müssen den an den Anbieter übergebenen Wert in einen useState Hook ändern. Jetzt ist der Wert unseres Themenkontexts ein useState Hook, dessen Standardwert „light“ ist.

 function App() { const themeHook = useState("light"); return ( <ThemeContext.Provider value = {themeHook}> <div> <Header /> <Main /> </div> </ThemeContext.Provider> ); }

Schreiben unserer ThemeToggler Komponente

Lassen Sie uns nun tatsächlich unsere ThemeToggler Komponente schreiben:

 import React,{useContext} from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { const[themeMode, setThemeMode] = useContext(ThemeContext); return( <div style = {themeTogglerStyle} onClick = {() => {setThemeMode(themeMode === "light"? "dark": "light")}}> <span title = "switch theme"> {themeMode === "light" ? "" : "️"} </span> </div> ); } export default ThemeToggler;

Da der Wert unseres Themenkontexts jetzt ein Hook ist, wenn wir useContext darauf aufrufen, wird ein Array zurückgegeben. Durch Destrukturierung konnten wir die Elemente aus dem Array greifen. Wir haben dann einen onClick -Event-Handler für unseren ThemeToggler . Mit diesem Code wird das Design unserer Anwendung umgeschaltet, wenn auf den Design-Umschalter geklickt wird.

Jetzt werden wir die verschiedenen Versionen unserer Main bearbeiten.

Bearbeiten unserer MainWithClass Komponente

  1. Die Version der MainWithClass Komponente, die die Class.contextType Methode verwendet:
     import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component{ constructor(){ super(); } static contextType = ThemeContext; render(){ const currentTheme = AppTheme[this.context[0]]; return( <main style={{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } }
  2. Die Version der MainWithClass Komponente, die die ThemeContext.Consumer Methode verwendet:
     import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component { constructor() { super(); this.state = {} } render() { return ( <ThemeContext.Consumer> { ([theme]) => { const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ) } } </ThemeContext.Consumer> ); } } export default Main;

Bearbeiten unserer MainWithFunction Komponente

Die MainWithFunction Komponente sollte wie folgt bearbeitet werden:

 import React, { useContext } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; const Main = () => { const theme = useContext(ThemeContext)[0]; const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } export default Main;

Fazit

Das ist es! Es ist uns gelungen, zwei Themenmodi für unsere React-App mithilfe der Context-API zu implementieren.

Dabei haben wir gelernt:

  • Was die Kontext-API ist und welches Problem sie löst;
  • Wann die Kontext-API verwendet werden sollte;
  • Context und in funktionalen und klassenbasierten Komponenten verwenden.

Weiterführende Literatur zu SmashingMag:

  • Styling in modernen Web-Apps
  • Erstellen von mobilen Apps mit Ionic und React
  • Erstellen Sie eine PWA mit Webpack und Workbox
  • Kennenlernen der MutationObserver-API