React开发:使用React Context API

React Context API作为实验功能已经存在了一段时间, 但是只有在React的16.3.0版本中, 它才可以在生产中安全使用。下面的文章将向你展示两个基本的网络商店应用程序, 一个使用Context API构建, 另一个不使用Context API。

这个新的API解决了一个主要问题-道具钻探。即使你不熟悉该术语, 但是如果你使用过React.js应用程序, 它也可能发生在你身上。道具钻探是通过使数据经过多层中间React组件层将数据从组件A传递到组件Z的过程。组件将间接接收道具, 而你, React Developer必须确保一切正常。

让我们探讨一下在没有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>
        );
    }
}

产品清单.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}>&uarr;</button>
        <button onClick={props.decrementPrice}>&darr;</button>
    </Fragment>
);

诚然, 这不是处理数据的最佳方法, 但我希望它能说明为什么钻探钻头很烂。那么, Context API如何帮助我们避免这种情况?

Context Web Store简介

让我们重构应用程序并演示其功能。简而言之, Context API允许你拥有一个数据存储所在的中央存储(是的, 就像Redux中一样)。商店可以直接插入任何组件中。你可以砍掉中间人!

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

重构非常容易-我们不必对组件的结构进行任何更改。但是, 我们确实需要创建一些新组件-提供者和使用者。

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。在其中, 我们使用一些值初始化状态, 你可以通过值将其共享给提供者组件。在我们的示例中, 我们共享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中定义的。

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.创建消费者

我们将需要再次导入上下文, 并用它包装我们的组件, 这会将context参数注入到组件中。之后, 这很简单。你使用上下文, 就像使用道具一样。它包含了我们在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.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应用程序的性能感兴趣, 请阅读srcminier William Wang同事撰写的《优化React性能指南》。

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?