Vielleicht haben Sie sich in letzter Zeit gefragt, was es mit dem ganzen Hype um Context auf sich hat und was das für Sie und Ihre React-Websites bedeuten könnte. Vor Context mussten Sie wahrscheinlich eine Drittanbieterbibliothek verwenden, wenn die Zustandsverwaltung über die Funktionalität von setState hinaus kompliziert wurde. Dank der neuesten Updates des großartigen React-Teams haben wir jetzt Context, was bei einigen Zustandsverwaltungsproblemen helfen kann.
Was löst Context? (What Does Context Solve?)
Wie verschiebt man den Zustand von einer Elternkomponente in eine Kindkomponente, die im Komponentenbaum verschachtelt ist? Sie wissen, dass Sie Redux zur Verwaltung des Zustands verwenden können, aber Sie müssen nicht in jeder Situation auf Redux zurückgreifen.
Es gibt einen Weg, dies ohne Redux oder ein anderes Zustandsverwaltungstool von Drittanbietern zu tun. Sie können Props verwenden!
Angenommen, die Funktion, die Sie implementieren möchten, hat eine Baumstruktur, ähnlich der unten dargestellten.

Der Zustand befindet sich in der App-Komponente und wird in den UserProfile- und UserDetails-Komponenten benötigt. Sie müssen ihn über Props durch den Baum übergeben. Wenn die Komponenten, die diesen Zustand benötigen, 10 Ebenen tief sind, kann dies mühsam, ermüdend und fehleranfällig werden. Jede Komponente sollte wie eine Blackbox sein – andere Komponenten sollten keine Zustände kennen, die sie nicht benötigen. Hier ist ein Beispiel für eine Anwendung, die das obige Szenario widerspiegelt.
class App extends React.Component {
state = {
user: {
username: 'jioke',
firstName: 'Kingsley',
lastName: 'Silas'
}
}
render() {
return(
<div>
<User user={this.state.user} />
</div>
)
}
}
const User = (props) => (
<div>
<UserProfile {...props.user} />
</div>
)
const UserProfile = (props) => (
<div>
<h2>Profile Page of {props.username}</h2>
<UserDetails {...props} />
</div>
)
const UserDetails = (props) => (
<div>
<p>Username: {props.username}</p>
<p>First Name: {props.firstName}</p>
<p>Last Name: {props.lastName}</p>
</div>
)
ReactDOM.render(<App />, document.getElementById("root"));
Wir übergeben den Zustand mit Props von einer Komponente zur anderen. Die User-Komponente benötigt den Zustand nicht, muss ihn aber über Props empfangen, damit er durch den Baum gelangt. Genau das wollen wir vermeiden.
Sehen Sie sich den Pen React Context API Pen 1 von Kingsley Silas Chijioke (@kinsomicrote) auf CodePen an.
Context zur Rettung! (Context to the Rescue!)
Die Context API von React ermöglicht es Ihnen, den Zustand in einem global erscheinenden Anwendungszustand zu speichern und ihn nur in den Komponenten abzurufen, die ihn benötigen, ohne ihn über Props durchreichen zu müssen.
Wir beginnen mit der Initialisierung eines neuen Contexts mit createContext() von React.
const UserContext = React.createContext({})
const UserProvider = UserContext.Provider
const UserConsumer = UserContext.Consumer
Dieser neue Context wird einer const-Variablen zugewiesen, in diesem Fall ist die Variable UserContext. Sie können sehen, dass keine Bibliothek installiert werden muss, da createContext() in React (ab Version 16.3.0) verfügbar ist.
Die Provider-Komponente macht den Context für Komponenten verfügbar, die ihn benötigen, und diese werden als Subscriber bezeichnet. Mit anderen Worten, die Provider-Komponente ermöglicht es Consumern, sich für Änderungen im Context zu registrieren. Denken Sie daran, dass der Context einem globalen Anwendungszustand ähnelt. Daher werden Komponenten, die keine Consumer sind, nicht für den Context registriert.
Wenn Sie lokal programmieren, sieht Ihre Context-Datei so aus.
import { createContext } from 'react'
const UserContext = createContext({})
export const UserProvider = UserContext.Provider
export const UserConsumer = UserContext.Consumer
Der Provider (The Provider)
Wir werden den Provider in unserer Elternkomponente verwenden, wo wir unseren Zustand haben.
class App extends React.Component {
state = {
user: {
username: 'jioke',
firstName: 'Kingsley',
lastName: 'Silas'
}
}
render() {
return(
<div>
<UserProvider value={this.state.user}>
<User />
</UserProvider>
</div>
)
}
}
Der Provider akzeptiert eine Wert-Prop, die an seine nachfolgenden Consumer-Komponenten weitergegeben wird. In diesem Fall übergeben wir den Benutzerzustand an die Consumer-Komponenten. Sie können sehen, dass wir den Zustand nicht als Props an die User-Komponente übergeben. Das bedeutet, dass wir die User-Komponente bearbeiten und die Props ausschließen können, da sie diese nicht benötigt.
const User = () => (
<div>
<UserProfile />
</div>
)
Der Consumer (The Consumer)
Mehrere Komponenten können sich für eine Provider-Komponente registrieren. Unsere UserProfile-Komponente muss den Context nutzen, daher wird sie sich dafür registrieren.
const UserProfile = (props) => (
<UserConsumer>
{context => {
return(
<div>
<h2>Profile Page of {context.username}</h2>
<UserDetails />
</div>
)
}}
</UserConsumer>
)
Die Daten, die wir über die Wert-Prop in den Provider eingespeist haben, werden dann im Context-Parameter der Funktion verfügbar gemacht. Wir können nun diesen nutzen, um auf den Benutzernamen des Benutzers in unserer Komponente zuzugreifen.
Die UserDetails-Komponente wird ähnlich aussehen wie die UserProfile-Komponente, da sie sich für denselben Provider registriert.
const UserDetails = () => (
<div>
<UserConsumer>
{context => {
return (
<div>
<p>Userame: {context.username}</p>
<p>First Name: {context.firstName}</p>
<p>Last Name: {context.lastName}</p>
</div>
)
}}
</UserConsumer>
</div>
)
Sehen Sie sich den Pen React Context API Pen 2 von Kingsley Silas Chijioke (@kinsomicrote) auf CodePen an.
Zustand aktualisieren (Updating State)
Was ist, wenn wir zulassen möchten, dass Benutzer ihren Vor- und Nachnamen ändern? Das ist auch möglich. Consumer-Komponenten können neu gerendert werden, wenn sich der Wert, der von der Provider-Komponente übergeben wird, ändert. Sehen wir uns ein Beispiel an.
Wir werden zwei Eingabefelder für den Vor- und Nachnamen in der Consumer-Komponente haben. Von der Provider-Komponente werden wir zwei Methoden haben, die den Zustand der Anwendung mithilfe der in den Eingabefeldern eingegebenen Werte aktualisieren. Genug geredet, lasst uns programmieren!
Unsere App-Komponente wird so aussehen.
class App extends React.Component {
state = {
user: {
username: 'jioke',
firstName: 'Kingsley',
lastName: 'Silas'
}
}
render() {
return(
<div>
<UserProvider value={
{
state: this.state.user,
actions: {
handleFirstNameChange: event => {
const value = event.target.value
this.setState(prevState => ({
user: {
...prevState.user,
firstName: value
}
}))
},
handleLastNameChange: event => {
const value = event.target.value
this.setState(prevState => ({
user: {
...prevState.user,
lastName: value
}
}))
}
}
}
}>
<User />
</UserProvider>
</div>
)
}
}
Wir übergeben ein Objekt, das Zustand und Aktionen enthält, an die Wert-Props, die der Provider empfängt. Die Aktionen sind Methoden, die ausgelöst werden, wenn ein onChange-Ereignis auftritt. Der Wert des Ereignisses wird dann verwendet, um den Zustand zu aktualisieren. Da wir entweder den Vornamen oder den Nachnamen aktualisieren möchten, müssen wir den Wert des anderen beibehalten. Dazu verwenden wir den ES6 Spread Operator, der es uns ermöglicht, den Wert des angegebenen Schlüssels zu aktualisieren.
Mit den neuen Änderungen müssen wir die UserProfile-Komponente aktualisieren.
const UserProfile = (props) => (
<UserConsumer>
{({state}) => {
return(
<div>
<h2>Profile Page of {state.username}</h2>
<UserDetails />
</div>
)
}}
</UserConsumer>
)
Wir verwenden ES6-Destrukturierung, um den Zustand aus dem vom Provider empfangenen Wert zu extrahieren.
Für die UserDetails-Komponente erhalten wir sowohl Zustand als auch Aktionen. Wir müssen auch zwei Eingabefelder hinzufügen, die auf ein onChange()-Ereignis hören und die entsprechenden Methoden aufrufen.
const UserDetails = () => {
return (
<div>
<UserConsumer>
{({ state, actions }) => {
return (
<div>
<div>
<p>Userame: {state.username}</p>
<p>First Name: {state.firstName}</p>
<p>Last Name: {state.lastName}</p>
</div>
<div>
<div>
<input type="text" value={state.firstName} onChange={actions.handleFirstNameChange} />
</div>
<div>
<input type="text" value={state.lastName} onChange={actions.handleLastNameChange} />
</div>
</div>
</div>
)
}}
</UserConsumer>
</div>
)
}
Verwendung von Standardwerten (Using Default Values)
Es ist möglich, Standardwerte beim Initialisieren von Context zu übergeben. Um dies zu tun, werden wir anstelle eines leeren Objekts an createContext() einige Daten übergeben.
const UserContext = React.createContext({
username: 'johndoe',
firstName: 'John',
lastName: 'Doe'
})
Um diese Daten in unserem Anwendungsbaum zu nutzen, müssen wir den Provider aus dem Baum entfernen. Unsere App-Komponente wird also so aussehen.
class App extends React.Component {
state = {
user: {
username: 'jioke',
firstName: 'Kingsley',
lastName: 'Silas'
}
}
render() {
return(
<div>
<User />
</div>
)
}
}
Sehen Sie sich den Pen React Context API Pen 4 von Kingsley Silas Chijioke (@kinsomicrote) auf CodePen an.
Die Daten, die in den Consumer-Komponenten verwendet werden, werden bei der Initialisierung eines neuen Contexts definiert.
Fazit
Wenn die Dinge kompliziert werden und Sie versucht sind, yarn install [<fügen Sie hier eine Drittanbieterbibliothek für Zustandsverwaltung ein>] auszuführen, halten Sie einen Moment inne – Sie haben React Context bereit. Glauben Sie mir nicht? Vielleicht glauben Sie Kent C. Dodds.
Für das endgültige Beispiel mit Standard-Context-Daten, wie würden Sie das wieder mit dem Anwendungszustand verknüpfen, wie Sie es im vorletzten Beispiel getan haben? Oder legen Sie die Context-Daten nur explizit fest, wenn sie vom Anwendungszustand getrennt sein sollen?
Ich war der Meinung,
setStateführe eine Zusammenführung durch, so dass Sie den Spread-Operator nicht benötigen würden.Ich habe den Spread-Operator verwendet, weil ich entweder den Vornamen oder den Nachnamen aktualisieren möchte, während der andere Wert unverändert bleibt – ohne den Zustand zu mutieren.
Sehen Sie diesen Pen.
Ausgezeichnete Punkte zu React Context.
Vorgestellt bei https://reactdom.com/issues/95