Arbeiten mit der React Context API

Veröffentlicht: 2022-03-11

Die React Context API gibt es schon seit einiger Zeit als experimentelles Feature, aber erst in Reacts Version 16.3.0 wurde es sicher, sie in der Produktion zu verwenden. Der folgende Artikel zeigt Ihnen zwei grundlegende Webshop-Apps, eine mit und eine ohne die Context-API.

Diese neue API löst ein großes Problem – Prop Drilling . Auch wenn Sie mit dem Begriff nicht vertraut sind, wenn Sie an einer React.js-App gearbeitet haben, ist es Ihnen wahrscheinlich passiert. Prop Drilling ist die Verarbeitung von Daten von Komponente A zu Komponente Z, indem sie durch mehrere Schichten von zwischengeschalteten React-Komponenten geleitet werden. Die Komponente erhält indirekt Requisiten und Sie , der React-Entwickler, müssen sicherstellen, dass alles richtig funktioniert.

Lassen Sie uns untersuchen, wie Sie häufig auftretende Probleme ohne die React Context API lösen würden.

App.js

 class App extends Component { state = { cars: { car001: { name: 'Honda', price: 100 }, car002: { name: 'BMW', price: 150 }, car003: { name: 'Mercedes', price: 200 } } }; incrementCarPrice = this.incrementCarPrice.bind(this); decrementCarPrice = this.decrementCarPrice.bind(this); incrementCarPrice(selectedID) { // a simple method that manipulates the state const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price + 1; this.setState({ cars }); } decrementCarPrice(selectedID) { // a simple method that manipulates the state const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price - 1; this.setState({ cars }); } render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to my web store</h1> </header> {/* Pass props twice */} <ProductList cars={this.state.cars} incrementCarPrice={this.incrementCarPrice} decrementCarPrice={this.decrementCarPrice} /> </div> ); } }

Produktliste .js

 const ProductList = props => ( <div className="product-list"> <h2>Product list:</h2> {/* Pass props twice */} <Cars cars={props.cars} incrementCarPrice={props.incrementCarPrice} decrementCarPrice={props.decrementCarPrice} /> {/* Other potential product categories which we will skip for this demo: */} {/* <Electronics /> */} {/* <Clothes /> */} {/* <Shoes /> */} </div> ); export default ProductList;

Autos.js

 const Cars = props => ( <Fragment> <h4>Cars:</h4> {/* Finally we can use data */} {Object.keys(props.cars).map(carID => ( <Car key={carID} name={props.cars[carID].name} price={props.cars[carID].price} incrementPrice={() => props.incrementCarPrice(carID)} decrementPrice={() => props.decrementCarPrice(carID)} /> ))} </Fragment> );

Auto.js

 const Cars = props => ( <Fragment> <p>Name: {props.name}</p> <p>Price: ${props.price}</p> <button onClick={props.incrementPrice}>&uarr;</button> <button onClick={props.decrementPrice}>&darr;</button> </Fragment> );

Zugegeben, dies ist nicht der beste Weg, um mit Ihren Daten umzugehen, aber ich hoffe, es zeigt, warum Prop Drilling scheiße ist . Wie kann uns die Context-API helfen, dies zu vermeiden?

Einführung in den Context Web Store

Lassen Sie uns die App umgestalten und demonstrieren, was sie kann. Kurz gesagt, die Kontext-API ermöglicht es Ihnen, einen zentralen Speicher zu haben, in dem Ihre Daten gespeichert sind (ja, genau wie in Redux). Der Store kann direkt in jede Komponente eingefügt werden. Sie können den Mittelsmann ausschalten!

Beispiel für zwei Zustandsflüsse: einer mit der React Context API und einer ohne

Das Refactoring ist ganz einfach – wir müssen keine Änderungen an der Struktur der Komponenten vornehmen. Wir müssen jedoch einige neue Komponenten erstellen – einen Anbieter und einen Verbraucher.

1. Initialisieren Sie den Kontext

Zuerst müssen wir den Kontext erstellen, den wir später verwenden können, um Anbieter und Verbraucher zu erstellen.

MyContext.js

 import React from 'react'; // this is the equivalent to the createStore method of Redux // https://redux.js.org/api/createstore const MyContext = React.createContext(); export default MyContext;

2. Erstellen Sie den Anbieter

Sobald dies erledigt ist, können wir den Kontext importieren und ihn verwenden, um unseren Anbieter zu erstellen, den wir MyProvider nennen. Darin initialisieren wir einen Zustand mit einigen Werten, die Sie über value prop unserer Provider-Komponente teilen können. In unserem Beispiel teilen wir this.state.cars zusammen mit einigen Methoden, die den Zustand manipulieren. Stellen Sie sich diese Methoden als Reducer in Redux vor.

MyProvider.js

 import MyContext from './MyContext'; class MyProvider extends Component { state = { cars: { car001: { name: 'Honda', price: 100 }, car002: { name: 'BMW', price: 150 }, car003: { name: 'Mercedes', price: 200 } } }; render() { return ( <MyContext.Provider value={{ cars: this.state.cars, incrementPrice: selectedID => { const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price + 1; this.setState({ cars }); }, decrementPrice: selectedID => { const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price - 1; this.setState({ cars }); } }} > {this.props.children} </MyContext.Provider> ); } }

Um den Provider für andere Komponenten zugänglich zu machen, müssen wir unsere App damit umhüllen (ja, genau wie in Redux). Wenn wir schon dabei sind, können wir den Zustand und die Methoden loswerden, weil sie jetzt in MyProvider.js definiert sind.

App.js

 class App extends Component { render() { return ( <MyProvider> <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to my web store</h1> </header> <ProductList /> </div> </MyProvider> ); } }

3. Erstellen Sie den Verbraucher

Wir müssen den Kontext erneut importieren und unsere Komponente damit umschließen, wodurch das Kontextargument in die Komponente eingefügt wird. Danach geht es ziemlich direkt weiter. Sie verwenden den Kontext auf die gleiche Weise, wie Sie Requisiten verwenden würden. Es enthält alle Werte, die wir in MyProducer geteilt haben, wir müssen es nur verwenden!

Autos.js

 const Cars = () => ( <MyContext.Consumer> {context => ( <Fragment> <h4>Cars:</h4> {Object.keys(context.cars).map(carID => ( <Car key={carID} name={context.cars[carID].name} price={context.cars[carID].price} incrementPrice={() => context.incrementPrice(carID)} decrementPrice={() => context.decrementPrice(carID)} /> ))} </Fragment> )} </MyContext.Consumer> );

Was haben wir vergessen? Die Produktliste! Hier zeigt sich der Nutzen. Wir geben keine Daten oder Methoden weiter. Die Komponente wird vereinfacht, da sie nur wenige Komponenten rendern muss.

ProductList.js

 const ProductList = () => ( <div className="product-list"> <h2>Product list:</h2> <Cars /> {/* Other potential product categories which we will skip for this demo: */} {/* <Electronics /> */} {/* <Clothes /> */} {/* <Shoes /> */} </div> );

Im Laufe dieses Artikels habe ich einige Vergleiche zwischen Redux und der Context-API angestellt. Einer der größten Vorteile von Redux ist die Tatsache, dass Ihre App einen zentralen Speicher haben kann, auf den von jeder Komponente aus zugegriffen werden kann. Mit der neuen Kontext-API haben Sie diese Funktionalität standardmäßig. Es wurde viel Aufhebens darum gemacht, dass die Context-API Redux obsolet machen wird.

Dies gilt möglicherweise für diejenigen unter Ihnen, die Redux nur für seine zentralen Speicherfunktionen verwenden. Wenn dies die einzige Funktion ist, für die Sie es verwendet haben, können Sie es jetzt durch die Kontext-API ersetzen und Prop Drilling vermeiden, ohne Bibliotheken von Drittanbietern zu verwenden.

Wenn Sie daran interessiert sind, die Leistung Ihrer React-Anwendung zu messen und zu optimieren, lesen Sie A Guide to Optimizing React Performance von Toptaler William Wang.