怎样使用React Context API

翻译:疯狂的技术宅
原文: 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}>&uarr;</button>
        <button onClick={props.decrementPrice}>&darr;</button>
    </Fragment>
);

固然,这不是处理数据的最好方式,但我但愿可以用它说明为何 prop drilling 不好劲。 那么 Context API 是如何帮咱们避免这种状况呢?微信

介绍Context Web Store

让咱们重构程序并演示它能够作些什么。 简而言之,Context API 容许你拥有一个存储数据的中央存储(是的,就像存储在 Redux 中同样)。你能够把任何组件直接插入到商店应用中,也能够切断 middleman!ide

两个状态流的示例:一个使用React Context API,另外一个不用

重构很是简单 —— 咱们没必要对组件的结构进行任何修改。可是咱们确实须要建立一些新组件:Provider 和 consumer。

1.初始化 Context

首先,咱们须要建立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;

2. 建立 Provider

完成后,咱们能够导入 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>
        );
    }
}

3. 建立 Consumer

咱们须要再次导入 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欢迎关注,天天都给你推送新鲜的前端技术文章

相关文章
相关标签/搜索