使用 React 上下文 API
已發表: 2022-03-11React Context API 作為一個實驗性特性已經存在了一段時間,但只有在 React 的 16.3.0 版本中,它才可以安全地在生產環境中使用。 下面的文章將向您展示兩個基本的網絡商店應用程序,一個使用 Context API 構建,一個沒有它。
這個新的 API 解決了一個主要問題——支柱鑽孔。 即使你不熟悉這個術語,如果你曾經開發過 React.js 應用程序,它也可能發生在你身上。 Prop 鑽取是通過多層中間 React 組件將數據從組件 A 獲取到組件 Z 的處理。 組件將間接接收道具,而您,React 開發人員將必須確保一切正常。
讓我們探討一下在沒有 React Context API 的情況下如何處理常見問題,
應用程序.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> ); } }
產品列表 .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;
汽車.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> );
汽車.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> );
誠然,這不是處理數據的最佳方式,但我希望它能說明為什麼螺旋鑽很爛。 那麼 Context API 如何幫助我們避免這種情況呢?
介紹 Context Web Store
讓我們重構應用程序並演示它可以做什麼。 簡而言之,Context API 允許您擁有一個存儲數據的中央存儲(是的,就像在 Redux 中一樣)。 商店可以直接插入到任何組件中。 你可以砍掉中間人!
重構非常容易——我們不必對組件的結構進行任何更改。 然而,我們確實需要創建一些新組件——提供者和消費者。
1.初始化上下文
首先,我們需要創建上下文,稍後我們可以使用它來創建提供者和消費者。

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.創建提供者
完成後,我們可以導入上下文並使用它來創建我們的提供者,我們稱之為 MyProvider。 在其中,我們使用一些值初始化一個狀態,您可以通過value prop 共享我們的提供程序組件。 在我們的示例中,我們共享this.state.cars
以及一些操縱狀態的方法。 將這些方法視為 Redux 中的 reducer。
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> ); } }
為了讓其他組件可以訪問提供程序,我們需要用它來包裝我們的應用程序(是的,就像在 Redux 中一樣)。 當我們這樣做時,我們可以擺脫狀態和方法,因為它們現在在 MyProvider.js 中定義。
應用程序.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. 創建消費者
我們需要再次導入上下文並用它包裝我們的組件,這會在組件中註入上下文參數。 之後,它非常簡單。 你使用context ,就像你使用props一樣。 它包含我們在 MyProducer 中共享的所有值,我們只需要使用它!
汽車.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> );
我們忘記了什麼? 產品清單! 這就是好處變得顯而易見的地方。 我們不傳遞任何數據或方法。 組件被簡化,因為它只需要渲染幾個組件。
產品列表.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> );
在本文的過程中,我對 Redux 和 Context API 進行了一些比較。 Redux 的最大優勢之一是您的應用程序可以有一個可以從任何組件訪問的中央存儲。 使用新的 Context API,您默認擁有該功能。 很多人都在炒作 Context API 將使 Redux 過時。
對於那些僅將 Redux 用於其中央存儲功能的人來說,這可能是正確的。 如果這是您使用它的唯一功能,您現在可以用 Context API 替換它,並避免在不使用第三方庫的情況下進行道具鑽探。
如果您對測量和優化 React 應用程序的性能感興趣,請閱讀 Toptaler William Wang 的A Guide to Optimizing React Performance 。