Praca z React Context API
Opublikowany: 2022-03-11Interfejs API React Context jest już od jakiegoś czasu funkcją eksperymentalną, ale dopiero w wersji 16.3.0 Reacta stało się bezpieczne w użyciu w środowisku produkcyjnym. Poniższy artykuł pokaże Ci dwie podstawowe aplikacje sklepu internetowego, jedną zbudowaną z Context API i drugą bez niego.
To nowe API rozwiązuje jeden główny problem – wiercenie pod śruby. Nawet jeśli nie znasz tego terminu, jeśli pracowałeś nad aplikacją React.js, prawdopodobnie ci się to przydarzyło. Prop drill to przetwarzanie danych z komponentu A do komponentu Z poprzez przepuszczanie ich przez wiele warstw pośredniczących komponentów React. Komponent otrzyma rekwizyty pośrednio, a Ty , React Developer, będziesz musiał upewnić się, że wszystko działa poprawnie.
Przyjrzyjmy się, jak poradziłbyś sobie z typowymi problemami bez React Context API,
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> ); } }
Lista produktów .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;
Cars.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> );
Car.js
const Cars = props => ( <Fragment> <p>Name: {props.name}</p> <p>Price: ${props.price}</p> <button onClick={props.incrementPrice}>↑</button> <button onClick={props.decrementPrice}>↓</button> </Fragment> );
To prawda, że nie jest to najlepszy sposób na przetwarzanie danych, ale mam nadzieję, że pokazuje, dlaczego wiercenie rekwizytów jest do bani . Jak więc Context API może pomóc nam tego uniknąć?
Przedstawiamy Context Web Store
Zrefaktoryzujmy aplikację i zademonstrujmy, co potrafi. W kilku słowach Context API pozwala mieć centralny magazyn, w którym znajdują się Twoje dane (tak, tak jak w Redux). Sklep można wstawić bezpośrednio do dowolnego komponentu. Możesz wyciąć pośrednika!
Refaktoryzacja jest dość prosta – nie musimy wprowadzać żadnych zmian w strukturze komponentów. Musimy jednak stworzyć kilka nowych komponentów – dostawcę i konsumenta.
1. Zainicjuj kontekst
Najpierw musimy stworzyć kontekst, który później będziemy mogli wykorzystać do tworzenia dostawców i konsumentów.

MójKontekst.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. Utwórz dostawcę
Gdy to zrobimy, możemy zaimportować kontekst i użyć go do utworzenia naszego dostawcy, którego nazywamy MyProvider. W nim inicjujemy stan z pewnymi wartościami, które możesz udostępniać za pomocą wartości prop naszego komponentu dostawcy. W naszym przykładzie udostępniamy this.state.cars
wraz z kilkoma metodami, które manipulują stanem. Pomyśl o tych metodach jako o reduktorach w Redux.
MójDostawca.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> ); } }
Aby dostawca był dostępny dla innych komponentów, musimy owinąć nim naszą aplikację (tak, tak jak w Redux). Skoro już przy tym jesteśmy, możemy pozbyć się stanu i metod, ponieważ są one teraz zdefiniowane w MyProvider.js.
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. Stwórz Konsumenta
Będziemy musieli ponownie zaimportować kontekst i otoczyć nim nasz komponent, co spowoduje wstrzyknięcie argumentu context w komponencie. Później jest to całkiem proste. Używasz kontekstu , tak samo jak używasz rekwizytów . Zawiera wszystkie wartości, które podzieliliśmy w MyProducer, wystarczy go użyć!
Cars.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> );
O czym zapomnieliśmy? Lista produktów! Tu widać korzyści. Nie przekazujemy żadnych danych ani metod. Komponent jest uproszczony, ponieważ wymaga renderowania tylko kilku komponentów.
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> );
W trakcie tego artykułu dokonałem kilku porównań między Redux a Context API. Jedną z największych zalet Redux jest to, że Twoja aplikacja może mieć centralny sklep, do którego można uzyskać dostęp z dowolnego komponentu. Dzięki nowemu interfejsowi API kontekstu masz tę funkcję domyślnie. Sporo było szumu, że Context API sprawi, że Redux stanie się przestarzały.
Może to dotyczyć tych z Was, którzy używają Redux tylko do funkcji centralnego sklepu. Jeśli to jedyna funkcja, do której go używałeś, możesz teraz zastąpić ją interfejsem API kontekstu i uniknąć drążenia rekwizytów bez korzystania z bibliotek innych firm.
Jeśli interesuje Cię pomiar i optymalizacja wydajności aplikacji React, przeczytaj Przewodnik po optymalizacji wydajności React autorstwa innego Toptalera, Williama Wanga.