In React ist der Kontext eine Möglichkeit, Daten zwischen Komponentenbäumen zu übergeben, ohne für jede Komponentenschicht manuell Requisiten hinzuzufügen. Der Kontext bietet eine Möglichkeit, bestimmte Werte zwischen Komponenten zu teilen, ohne sie explizit durch die Komponente weiterleiten zu müssen im Baum.
Die Betriebsumgebung dieses Tutorials: Windows 10-System, Reaktionsversion 17.0.1, Dell G3-Computer.
Kontext bietet eine Möglichkeit, Daten zwischen Komponentenbäumen zu übergeben, ohne manuell Requisiten zu jeder Komponentenebene hinzuzufügen. In einer typischen React-Anwendung werden Daten von oben nach unten (vom übergeordneten zum untergeordneten Element) über Requisiten weitergeleitet. Dieser Ansatz ist jedoch für bestimmte Arten von Eigenschaften (z. B. Gebietsschemaeinstellungen, UI-Design) äußerst umständlich. Diese Eigenschaften werden von vielen Komponenten in benötigt die Anwendung. Der Kontext bietet eine Möglichkeit, solche Werte zwischen Komponenten zu teilen, ohne dass Requisiten explizit durch jede Ebene des Komponentenbaums geleitet werden müssen.
Der Kontext dient dazu, Daten zu teilen, die „global“ für einen Komponentenbaum sind, beispielsweise der aktuell authentifizierte Benutzer, das Thema oder die bevorzugte Sprache. Im folgenden Code passen wir beispielsweise den Stil einer Schaltflächenkomponente manuell über ein „Theme“-Attribut an
class App erweitert React.Component { render() { return <Toolbar theme="dark" />;function Toolbar(props) { // Die Toolbar-Komponente akzeptiert eine zusätzliche „Theme“-Eigenschaft, die dann an den ThemedButton übergeben wird Komponente. // Es wäre problematisch, wenn jede einzelne Schaltfläche in der Anwendung den Wert des Themas kennen müsste, // da dieser Wert an alle Komponenten weitergegeben werden müsste. return ( <p> <ThemedButton theme={props.theme} /> </p> );}class ThemedButton erweitert React.Component { render() { return <Button theme={this.props.theme} />; }// Requisiten durchlaufen: App -> Symbolleiste -> ThemedButton// Wenn die Verschachtelung sehr tief ist, müssen die Requisiten Schicht für Schicht übergeben werden, auch wenn die Requisiten in der Mitte nicht benötigt werden, erscheint dies sehr umständlich.Mithilfe des Kontexts können wir die Weitergabe von Requisiten durch Zwischenelemente vermeiden
// Der Kontext ermöglicht es uns, Werte tief in den Komponentenbaum zu übergeben, ohne sie explizit durch jede Komponente weiterleiten zu müssen. // Einen Kontext für das aktuelle Thema erstellen („hell“ ist der Standardwert). const ThemeContext = React.createContext('light');class App erweitert React.Component { render() { // Verwenden Sie einen Provider, um das aktuelle Thema an den folgenden Komponentenbaum zu übergeben. // Egal wie tief, jede Komponente kann diesen Wert lesen. // In diesem Beispiel übergeben wir „dark“ als aktuellen Wert. return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> }}// Die Zwischenkomponente muss das weiterzugebende Thema nicht mehr angeben. function Toolbar() { return ( <p> <ThemedButton /> </p> );}class ThemedButton erweitert React.Component { // Geben Sie contextType an, um den aktuellen Theme-Kontext zu lesen. // React findet den nächstgelegenen Theme-Anbieter und verwendet seinen Wert. // In diesem Beispiel ist der aktuelle Theme-Wert „dark“. static contextType = ThemeContext; render() { return <Button theme={this.context} />// Sie können auch ThemedButto.contextType = ThemeContext;Erstellen Sie ein Kontextobjekt. Wenn React eine Komponente rendert, die dieses Kontextobjekt abonniert hat, liest die Komponente den aktuellen Kontextwert vom passenden Provider, der ihr im Komponentenbaum am nächsten liegt.
Nur wenn es in der Baumstruktur, in der sich die Komponente befindet, keinen passenden Provider gibt, wird der Parameter defaultValue wirksam. Dies hilft beim Testen von Komponenten, ohne einen Provider zum Umschließen der Komponente zu verwenden. Hinweis: Wenn undefiniert an den Wert des Anbieters übergeben wird, wird der Standardwert der konsumierenden Komponente nicht wirksam.
Jedes Kontextobjekt gibt eine Provider-React-Komponente zurück, die es konsumierenden Komponenten ermöglicht, Kontextänderungen zu abonnieren.
Der Anbieter empfängt ein Wertattribut und übergibt es an die konsumierende Komponente. Ein Anbieter kann eine entsprechende Beziehung mit mehreren Verbraucherkomponenten haben. Es können auch mehrere Anbieter verschachtelt verwendet werden, wobei die innere Schicht die Daten der äußeren Schicht überschreibt.
Wenn sich der Wert des Anbieters ändert, werden alle darin enthaltenen konsumierenden Komponenten neu gerendert. Weder der Anbieter noch seine internen Verbraucherkomponenten unterliegen der Funktion ShouldComponentUpdate, sodass die Verbraucherkomponente auch dann aktualisiert werden kann, wenn die Aktualisierung ihrer Vorgängerkomponente beendet wird.
Das in der Klasse gemountete contextType-Attribut wird einem von React.createContext() erstellten Context-Objekt neu zugewiesen. Dadurch können Sie this.context verwenden, um den Wert des neuesten Kontexts zu nutzen. Sie können in jedem Lebenszyklus darauf zugreifen, einschließlich der Renderfunktion
import MyContext from './MyContext';class MyClass erweitert React.Component { ComponentDidMount() { let value = this.context /* Nachdem die Komponente gemountet wurde, verwenden Sie den Wert der MyContext-Komponente, um einige Nebeneffektoperationen auszuführen*; / } ComponentDidUpdate() { let value = this.context; /* ... */ } ComponentWillUnmount() { let value = this.context; /* ... */ } render() { let value = this.context ; /* Basierend auf dem Wert der MyContext-Komponente rendern*/ } // Oder verwenden Sie static contextType = MyContext;}MyClass.contextType = MyContext;Hier können React-Komponenten auch Kontextänderungen abonnieren. Dadurch können Sie den Kontext in Funktionskomponenten abonnieren.
Dies erfordert die Funktion eines Kindes. Diese Funktion empfängt den aktuellen Kontextwert und gibt einen React-Knoten zurück. Der an die Funktion übergebene Wert entspricht dem Wert, der vom Anbieter bereitgestellt wird, der diesem Kontext am nächsten in der Komponentenstruktur liegt. Wenn kein entsprechender Provider vorhanden ist, entspricht der Wertparameter dem an createContext() übergebenen defaultValue.
Das Kontextobjekt akzeptiert eine Eigenschaft namens displayName, die vom Typ string ist. React DevTools verwendet diese Zeichenfolge, um zu bestimmen, welcher Kontext angezeigt werden soll.
Die folgende Komponente wird in DevTools als MyDisplayName angezeigt
const MyContext = React.createContext(/* some value */);MyContext.displayName = 'MyDisplayName';<MyContext.Provider> // "MyDisplayName.Provider" in DevTools <MyContext.Consumer> // "MyDisplayName.Consumer" In DevToolsFür das obige Theme-Beispiel kann eine komplexere Nutzung mithilfe dynamischer Werte erreicht werden.
theme-context.js
export const themes = { light: { foreground: '#000000', background: '#eeeeee', }, dark: { foreground: '#ffffff', background: '#222222', },};export const ThemeContext = React .createContext(themes.dark); // Dies ist der Standardwertthemed-button.js
import { ThemeContext } from './theme-context'; class ThemedButton erweitert React.Component { let props = this.props; // Den Standardwert in ThemeContext abrufen let theme = this.context; {...props} style={{backgroundColor: theme.background}} /> } // static contextType = ThemeContext;}ThemedButton.contextType = ThemeContext;export default ThemedButton;app.js
import { ThemeContext, themes } from './theme-context';import ThemedButton from './themed-button';// Eine Zwischenkomponentenfunktion mit ThemedButton Toolbar(props) { return ( <ThemedButton onClick={props.changeTheme } > Theme ändern </ThemedButton> );} class App erweitert React.Component { constructionor(props) { super(props); this.state = { theme: themes.light, }; .setState(state => ({ theme: state.theme === themes.dark ? themes.light : themes.dark, }) }; // Wird von der ThemedButton-Schaltflächenkomponente im ThemeProvider verwendet value in state, // und externe Komponenten verwenden den Standard-Theme-Wert return ( <Page> <ThemeContext.Provider value={this.state.theme}> <Toolbar changeTheme={this.toggleTheme} /> </ThemeContext .Provider> <Section> <ThemedButton /> </Section> </Page> ); }}ReactDOM.render(<App />, document.root);// Von ThemeContext.Provider verpackte Komponenten können ThemeContext verwenden. Der Wert// in der Symbolleiste und ThemedButton können this.context verwenden, um den Wert abzurufen. // Beachten Sie, dass die Methode zum Aktualisieren des Status darin besteht, ihn über Requisiten weiterzugeben, und die Aktualisierung wird durch untergeordnete Komponenten ausgelöst. Die Methode zur Aktualisierung des Kontexts wird weiter unten erläutertIm obigen Beispiel übergeben wir eine Aktualisierungsfunktion über Requisiten, um den Wert von Themen in der App zu ändern. Wir wissen, dass es notwendig ist, den Kontext einer Komponente zu aktualisieren, die tief im Komponentenbaum verschachtelt ist. In diesem Szenario können Sie eine Funktion durch den Kontext übergeben, damit die Verbraucherkomponente den Kontext aktualisiert.
theme-context.js
// Stellen Sie sicher, dass die an createContext übergebene Standardwertdatenstruktur mit den aufrufenden Komponenten (Konsumenten) übereinstimmt! export const ThemeContext = React.createContext({ theme: themes.dark, toggleTheme: () => {}, // Methode zum Aktualisieren des Themes definieren, weitergeben});theme-toggler-button.js
import { ThemeContext } from './theme-context';function ThemeTogglerButton() { // Die Theme Toggler-Schaltfläche erhält nicht nur den Theme-Wert, sondern auch eine toggleTheme-Funktion aus dem Kontext (app.js-Teil unten) return ( < ThemeContext.Consumer> {({theme, toggleTheme}) => ( <button onClick={toggleTheme} style={{backgroundColor: theme.background}}> Thema umschalten </button> )} </ThemeContext.Consumer> ); }Standard-ThemeTogglerButton exportieren;app.js
import { ThemeContext, themes } from './theme-context';import ThemeTogglerButton from './theme-toggler-button';class App erweitert React.Component { constructionor(props) { super(props); this.toggleTheme = ( ) => { this.setState(state => ({ theme: state.theme === themes.dark ? themes.light : themes.dark, })); Wird an den Kontextanbieter übergeben. this.state = { theme: themes.light, toggleTheme: this.toggleTheme, // Definieren Sie die Update-Funktion und übergeben Sie sie über den Kontext }; } render() { // Der gesamte Status wird an den Anbieter übergeben return ( < ThemeContext. Provider value={this.state}> <Content /> </ThemeContext.Provider> );}function Content() { return ( <p> <ThemeTogglerButton /> </p> );}ReactDOM.render( <App />, document.root);Um sicherzustellen, dass der Kontext schnell neu gerendert wird, muss React den Kontext jeder Verbraucherkomponente zu einem separaten Knoten im Komponentenbaum machen.
// Themenkontext, das Standardthema ist „light“ value const ThemeContext = React.createContext('light'); // Benutzeranmeldekontext const UserContext = React.createContext({ name: 'Guest',}); React .Component { render() { const { signiertInUser, theme } = this.props; // App-Komponente, die den anfänglichen Kontextwert zurückgibt ( <ThemeContext.Provider value={theme}> <UserContext.Provider value={signedInUser} > <Layout /> </UserContext.Provider> </ThemeContext.Provider> ); }}function Layout() { return ( <p> <Sidebar /> <Content /> </p> );}// Eine Komponente kann mehrere Kontextfunktionen verbrauchen Content() { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext . Consumer> )} </ThemeContext.Consumer> );}Wenn zwei oder mehr Kontextwerte häufig zusammen verwendet werden, sollten Sie erwägen, eine eigene Renderkomponente zu erstellen, um diese Werte bereitzustellen.
Da der Kontext eine Referenzidentität verwendet, um zu entscheiden, wann gerendert werden soll, kann es einige Fallstricke geben, die ein unerwartetes Rendering in der Verbraucherkomponente auslösen können, wenn die übergeordnete Komponente des Anbieters erneut gerendert wird. Beispielsweise rendert der folgende Code jedes Mal, wenn der Provider neu gerendert wird, alle folgenden Verbraucherkomponenten neu, da das Wertattribut immer einem neuen Objekt zugewiesen wird
class App erweitert React.Component { render() { return ( <MyContext.Provider value={{something: 'something'}}> <Toolbar /> </MyContext.Provider> }}Um diese Situation zu verhindern, wird der Wertstatus auf den Status des übergeordneten Knotens heraufgestuft.
class App erweitert React.Component { constructionor(props) { super(props); // Nach mehreren Renderings bleibt der Status erhalten. Wenn sich der Wert nicht ändert, werden die folgenden Verbraucherkomponenten this.state = { nicht erneut rendern. value: { Something: 'something'}, }; render() { return ( <Provider value={this.state.value}> <Toolbar /> </Provider> }}