Hallo👋🏻,
Dieser Artikel richtet sich speziell an Anfänger, die effektivere Methoden zur Verwaltung des Status zwischen mehreren Komponenten erlernen möchten. Er befasst sich auch mit dem häufigen Problem des Prop Drilling, das die Wartung und das Verständnis Ihres Codes erschweren kann. Beginnen wir mit der Frage, welche Art von Problem die Context-API löst.
Wenn Sie das Videoformat bevorzugen, finden Sie hier das Tutorial, das Sie auf meinem YouTube-Kanal ansehen können.👇🏻
Sie wissen, dass Sie manchmal Daten von einer übergeordneten Komponente an eine untergeordnete Komponente weitergeben müssen und dabei Props durch eine Reihe von Komponenten dazwischen übergeben müssen? Das nennt man Prop Drilling und kann schnell unübersichtlich werden. Lassen Sie uns dies anhand eines Beispiels verdeutlichen.
Stellen Sie sich vor, Sie haben, wie im Diagramm gezeigt, einige Daten in der App
Komponente abgerufen, die sich an der Wurzel Ihrer Anwendung befindet. Wenn nun eine tief verschachtelte Komponente, beispielsweise die Grandchild
Komponente, auf diese Daten zugreifen muss, würden Sie sie normalerweise als Props durch die Parent
und Child
Komponenten weiterreichen, bevor sie Grandchild
erreichen. Dies kann unschön werden, wenn Ihre App wächst.
Hier ist eine weitere visuelle Darstellung:
Im obigen Beispiel benötigt die Profile
Benutzerdaten, aber diese Daten müssen zuerst durch die App
und Navigation
, obwohl diese Zwischenkomponenten die Daten selbst nicht verwenden. Wie können wir das also bereinigen? Hier kommt die Context API ins Spiel.
Stützenbohrungen:
Mit der Context API in React.js können Sie Daten zwischen Komponenten weitergeben, ohne sie als Props durch jede Ebene des Komponentenbaums weitergeben zu müssen. Es funktioniert wie ein globales Statusverwaltungssystem, in dem Sie Ihren Status in einem Kontextobjekt definieren und dann überall im Komponentenbaum problemlos darauf zugreifen können. Lassen Sie uns dies anhand eines Beispiels verstehen.
Wie Sie im Diagramm sehen können, haben wir ein Kontextobjekt, das Daten speichert, auf die mehrere Komponenten zugreifen können. Diese Daten werden von APIs oder Diensten von Drittanbietern abgerufen. Bevor wir in einer Komponente auf diese Kontextdaten zugreifen können, müssen wir alle Komponenten, die diese Daten benötigen, in eine Kontextanbieterkomponente einbinden.
Wenn wir nur auf Daten in den Navigations- und Profilkomponenten zugreifen müssen, müssen wir die App-Komponente nicht umschließen. Sobald Sie die relevanten Komponenten mit dem ContextProvider
umschlossen haben, können Sie direkt auf die Kontextdaten in jeder Komponente zugreifen, die sie verwendet. Keine Sorge, wenn Sie es noch nicht verstehen; lassen Sie uns in den Code eintauchen und ihn in Aktion sehen.
Lassen Sie uns zunächst eine React-App mit Vite.js erstellen. Kopieren Sie einfach die folgenden Befehle, um das Projekt einzurichten.
npm create vite@latest
cd project_name // to change to project directory npm install npm run dev
Anschließend können Sie Ihren Entwicklungsserver http://localhost:5173
in Ihrem Browser öffnen.
Lassen Sie uns zunächst die erforderlichen Ordner erstellen. Hier ist die Ordnerstruktur unseres Projekts.
src | components | context
Erstellen wir im Komponentenordner die Datei Profile.jsx
und fügen den folgenden Code hinzu.
import React from 'react' const Profile = () => { return ( <div>Profile</div> ) } export default Profile
Erstellen Sie eine weitere Komponente namens Navbar.jsx
im Komponentenordner.
import Profile from './Profile' const Navbar = () => { return ( <nav style={{ display: "flex", justifyContent: "space-between", alignItems: "center", width: "90%", height: "10vh", backgroundColor: theme === "light" ? "#fff" : "#1b1b1b", color: theme === "light" ? "#1b1b1b" : "#fff", border: "1px solid #fff", borderRadius: "5px", padding: "0 20px", marginTop: "40px", }}> <h1>LOGO</h1> <Profile /> </nav> ) } export default Navbar
Importieren wir diese <Navbar />
-Komponente in die Datei App.jsx
.
import Navbar from "./components/Navbar"; function App() { return ( <main style={{ display: "flex", flexDirection: "column", justifyContent: "start", alignItems: "center", height: "100vh", width: "100vw", }} > <Navbar /> </main> ); } export default App;
Grundsätzlich ist die Komponente <Profile />
das untergeordnete Element der Komponente <Navbar />
und <Navbar />
ist das untergeordnete Element der Komponente <App />
.
Erstellen wir die Datei UserContext.jsx
im context
. Fügen Sie der Datei den folgenden Code hinzu.
import { createContext, useEffect, useState } from "react"; export const UserContext = createContext(); export const UserProvider = ({ children }) => { const [user, setUser] = useState(null); const fetchUserData = async (id) => { const response = await fetch( `https://jsonplaceholder.typicode.com/users/${id}` ).then((response) => response.json()); console.log(response); setUser(response); }; useEffect(() => { fetchUserData(1); }, []); return ( <UserContext.Provider value={{ user, fetchUserData }} > {children} </UserContext.Provider> ); };
createContext
ein leeres UserContext
Objekt. Wir stellen sicher, dass wir es aus react
importieren. Wir können dem Kontextobjekt Standardwerte hinzufügen, aber wir lassen es vorerst null.
UserProvider
, der einen Provider mit UserContext
zurückgibt, wie etwa UserContext.Provider
. Er umschließt die untergeordneten Komponenten und im Wert können wir alles übergeben, was wir in den untergeordneten Komponenten verwenden möchten.
fetchUserData
akzeptiert id
und verwendet diese ID, um die Benutzerdaten abzurufen. Dann speichern wir die Antwort im user
.
fetchUserData
im useEffect
auf, sodass beim Laden der Seite die Funktion aufgerufen wird und die Daten in user
eingefügt werden.
Lassen Sie uns nun diesen Kontext in der Komponente <App />
verwenden. Umschließen Sie die Komponente <NavBar />
mit <UserProvider />
; dasselbe wie im folgenden Code:
<UserProvider> <Navbar /> </UserProvider>
Lassen Sie uns den user
in der Komponente <Profile />
verwenden. Dazu verwenden wir useContext
Hook. Dieser nimmt UserContext
und stellt die Werte bereit, die wir im UserProvider
übergeben haben, wie z. B. user
und fetchUserData
-Funktion. Denken Sie daran, dass wir die Komponente <Profile />
nicht umschließen müssen, da sie sich bereits in der Komponente <Navbar />
befindet, die bereits mit dem Provider umschlossen ist.
Öffnen Sie Profile.jsx
und fügen Sie den folgenden Code hinzu.
const { user } = useContext(UserContext); if (user) { return ( <span style={{ fontWeight: "bold", }} > {user.name} </span> ); } else { return <span>Login</span>; }
Hier verwenden wir den user
aus dem UserContext
. Wir zeigen den Benutzernamen an, wenn user
vorhanden ist, andernfalls zeigen wir nur eine Anmeldenachricht an. Wenn Sie jetzt die Ausgabe sehen, sollte in der Navigationsleistenkomponente ein Benutzername vorhanden sein. Auf diese Weise können wir jeden Status direkt verwenden, der im Kontext einer beliebigen Komponente steht. Die Komponente, die diesen Status verwendet, sollte in <Provider />
eingeschlossen werden.
Sie können auch mehrere Kontexte verwenden. Sie müssen die Anbieterkomponenten lediglich in eine andere Anbieterkomponente einbinden, wie im folgenden Beispiel gezeigt.
<ThemeProvider> <UserProvider> <Navbar /> </UserProvider> </ThemeProvider>
Im obigen Beispiel verwenden wir <ThemeProvider />
, das den Designstatus verwaltet.
Sie können sich das obige YouTube-Video ansehen, um das vollständige Beispiel der Verwendung mehrerer Kontextanbieter zu sehen.
Es gibt ein Problem, das auftritt, wenn Sie die Context API in mehreren Komponenten verwenden. Immer wenn sich der Status oder Wert in der Context API ändert, werden alle Komponenten, die diesen bestimmten Kontext abonniert haben, neu gerendert, auch wenn nicht alle Komponenten den geänderten Status verwenden. Um dieses Problem des erneuten Renderings zu verstehen, erstellen wir eine <Counter />
Komponente, die Kontext zum Speichern und Anzeigen von Zählwerten verwendet.
Schauen Sie sich das folgende Beispiel an. Sie können eine Datei Counter.jsx
im Komponentenordner erstellen und den folgenden Code einfügen.
import { createContext, memo, useContext, useState } from "react"; const CountContext = createContext(); const CountProvider = ({ children }) => { const [count, setCount] = useState(0); return ( <CountContext.Provider value={{ count, setCount }}> {children} </CountContext.Provider> ); }; function CountTitle() { console.log("This is Count Title component"); return <h1>Counter Title</h1>; } function CountDisplay() { console.log("This is CountDisplay component"); const { count } = useContext(CountContext); return <div>Count: {count}</div>; } function CounterButton() { console.log("This is CounterButton component"); const { count, setCount } = useContext(CountContext); return ( <> <CountTitle /> <CountDisplay /> <button onClick={() => setCount(count + 1)}>Increase</button> </> ); } export default function Counter() { return ( <CountProvider> <CounterButton /> </CountProvider> ); }
Im obigen Code:
CountContext
Objekt mit createContext.
CountProvider,
haben wir einen Status zum Speichern von Zählwerten. Wir senden count
und die setCount
Methode über value prop an die untergeordneten Komponenten.
Wir haben Komponenten separat erstellt, um zu sehen, wie oft einzelne Komponenten erneut gerendert werden.
<CountTitle />
: Diese Komponente zeigt nur den Titel an und verwendet keine Werte aus dem Kontext.
<CountDisplay />
: Diese Komponente zeigt Zählwerte an und verwendet count
aus dem Kontext.
<CounterButton />
: Diese Komponente rendert sowohl die obige Komponente als auch eine Schaltfläche, die die Zählwerte mit setCount
erhöht.
Am Ende kapseln wir die Komponente <CounterButton />
in die Komponente CountProvider
ein, damit die anderen Komponenten auf die Zählwerte zugreifen können.
Wenn Sie nun den Code ausführen und auf die Schaltfläche Increase
klicken, sehen Sie in den Protokollen, dass jede Komponente bei jeder Statusänderung neu gerendert wird. <CountTitle />
verwendet nicht einmal Zählwerte, wird aber neu gerendert. Dies geschieht, weil die übergeordnete Komponente von <CountTitle />
also <CounterButton />
den Wert von count verwendet und aktualisiert und deshalb neu gerendert wird.
Wie können wir dieses Verhalten optimieren? Die Antwort lautet memo
. Mit dem React- memo
können Sie das erneute Rendern einer Komponente überspringen, wenn ihre Eigenschaften unverändert bleiben. Fügen wir nach der Komponente <CountTitle />
die folgende Zeile hinzu.
const MemoizedCountTitle = React.memo(CountTitle)
Ersetzen Sie nun in der Komponente <CounterButton />
, in der wir die Komponente <CountTitle />
rendern, <CountTitle />
durch <MemoizedCountTitle />
wie im folgenden Code:
<> <MemoizedCountTitle /> <CountDisplay /> <button onClick={() => setCount(count + 1)}>Increase</button> </>
Wenn Sie jetzt die Anzahl erhöhen und die Protokolle überprüfen, sollten Sie feststellen können, dass die Komponente <CountTitle />
nicht mehr gerendert wird.
Redux
ist eine State-Management-Bibliothek für komplexes State-Management mit besser vorhersehbaren State-Übergängen. Die Context-API hingegen ist für einfaches State-Management und die Weitergabe von Daten durch den Komponentenbaum ohne Prop Drilling konzipiert. Also, wann soll man was wählen?
Es gibt außerdem noch eine weitere Bibliothek, die ebenfalls eine beliebte Option für die Statusverwaltung ist: React Recoil .
Wenn Sie mehr über React Recoil erfahren möchten, lassen Sie es mich in den Kommentaren wissen und ich werde basierend auf Ihrem Feedback ausführliche Tutorials zu diesem Thema erstellen.
Die Context API React.js
bietet eine leistungsstarke und effiziente Möglichkeit, Zustände über mehrere Komponenten hinweg zu verwalten und löst damit effektiv das Problem des Prop Drilling. Durch die Verwendung der Context API können Sie Ihren Code vereinfachen, unnötige Neurendervorgänge reduzieren und die Gesamtleistung der Anwendung verbessern.
Während die Context API ideal für einfaches Statusmanagement ist, können komplexere Anwendungen von der Verwendung von Redux
oder anderen Statusmanagement-Bibliotheken wie React Recoil
profitieren. Wenn Sie wissen, wann und wie Sie diese Tools verwenden, können Sie besser wartbare und skalierbare React-Anwendungen erstellen.
Vielen Dank für das Lesen dieses Artikels. Ich hoffe, Sie fanden ihn hilfreich. Wenn Sie daran interessiert sind, React, Redux und Next.js zu lernen und Projekte damit zu erstellen, können Sie meinen YouTube-Kanal hier besuchen: CodeBucks
Hier sind meine anderen Artikel, die Sie vielleicht lesen möchten:
So implementieren Sie sanftes Scrollen in Next.js mit Lenis und GSAP
Top 10 der beliebtesten VS Code-Themen, die Sie ausprobieren sollten
Besuchen Sie meinen persönlichen Blog: DevDreaming