翻譯:瘋狂的技術宅
原文: https://www.toptal.com/react/...
本文首發微信公衆號:jingchengyideng
歡迎關注,天天都給你推送新鮮的前端技術文章javascript
React Context API 如今已經成爲一個實驗性功能,可是隻有在 React 16.3.0 中才能用在生產中。本文將向你展現兩個基本的 Web 商店應用程序,一個使用了 Context API 進行構建,另外一個則不用。html
這個新的API解決了一個嚴重的問題 ——prop drilling。 即便你不熟悉這個術語,若是你曾經用 React.js 作過開發,它可能就已經在你身上發生過了。 Prop drilling 是經過將數據傳遞到多箇中間 React 組件層,將數據從組件A 獲取到組件 Z 的過程。 組件將間接的接收props,而你必須確保一切正常。前端
咱們先探討如何在沒有 React Context API 的狀況下處理常見問題:java
App.jsreact
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> ); } }
ProductList .jsweb
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.jsredux
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.jsapi
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> );
固然,這不是處理數據的最好方式,但我但願可以用它說明爲何 prop drilling 不好勁。 那麼 Context API 是如何幫咱們避免這種狀況呢?微信
讓咱們重構程序並演示它能夠作些什麼。 簡而言之,Context API 容許你擁有一個存儲數據的中央存儲(是的,就像存儲在 Redux 中同樣)。你能夠把任何組件直接插入到商店應用中,也能夠切斷 middleman!ide
重構很是簡單 —— 咱們沒必要對組件的結構進行任何修改。可是咱們確實須要建立一些新組件:Provider 和 consumer。
首先,咱們須要建立context,後面可使用它來建立 Provider 和 consumer。
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;
完成後,咱們能夠導入 context 並用它來建立咱們的 provider,咱們稱之爲 MyProvider。在裏面使用一些值初始化一個狀態,你能夠經過 value prop 共享咱們的 provider 組件。 在例子中,咱們將共享 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> ); } }
爲了使 provider 能夠訪問其餘組件,咱們須要用它包裝咱們的應用程序(沒錯,就像 Redux 同樣)。咱們能夠擺脫這些狀態和方法,由於它們在 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> ); } }
咱們須要再次導入 context 並用它包裝咱們的組件,它會在組件中注入context 參數。 以後,它很是直接。 你使用 context 就像用 props 同樣。 它包含咱們在 MyProducer 中共享的全部值,咱們所須要作的只是去使用它!
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> );
咱們彷佛忘記了什麼?是 ProductList !它使好處變得很是明顯。 咱們沒必要傳遞任何數據或方法。這個組件被簡化,由於它只須要去渲染一些組件。
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> );
在本文中,我對 Redux 和 Context API 進行了一些比較。 Redux 最大的優點之一就是你的應用能夠擁有一個能夠從任何組件訪問的中央存儲。而使用新的 Context API,默認狀況下你已經有了這個功能。 在巨大的宣傳攻勢下 Context API 將會使 Redux 變得過期。
對於那些只把 Redux 做爲中央存儲功能的人來講,可能確實如此。 若是你只使用 Redux 的這一個功能,如今可使用 Context API 替換它,並避免在不使用第三方庫的狀況下進行 prop drilling。
本文首發微信公衆號:jingchengyideng歡迎關注,天天都給你推送新鮮的前端技術文章