Eine Einführung in die Kontext-API von React
Veröffentlicht: 2022-03-10Fü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 →
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:
- Die erste Methode ist durch
Class.contextType
.
Um diese Methode zu verwenden, weisen wir das Context-Objekt aus unseremThemeContext
der EigenschaftcontextType
unserer Klasse zu. Danach können wir mitthis.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> ); } }
NachdemThemeContext
der EigenschaftcontextType
unserer Klasse zugewiesen hatte, habe ich das aktuelle Designobjekt in der VariablencurrentTheme
.
Jetzt holen wir uns die Farben aus der VariablencurrentTheme
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. - 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 unsererMainWithClass
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 unseresThemeContext
verwendet, den wir als „theme“ aliasiert haben, und wir haben die Farbwerte für diesen Themenmodus abgerufen und sie der VariablencurrentTheme
. 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
- Die Version der
MainWithClass
Komponente, die dieClass.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> ); } }
- Die Version der
MainWithClass
Komponente, die dieThemeContext.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