怎样使用React Context API

更新日期: 2019-02-20阅读: 2.2k标签: react

react Context API 现在已经成为一个实验性功能,但是只有在 React 16.3.0 中才能用在生产中。本文将向你展示两个基本的 Web 商店应用程序,一个使用了 Context api 进行构建,另一个则不用。

这个新的API解决了一个严重的问题 ——prop drilling。 即使你不熟悉这个术语,如果你曾经用 React.js 做过开发,它可能就已经在你身上发生过了。 Prop drilling 是通过将数据传递到多个中间 React 组件层,将数据从组件A 获取到组件 Z 的过程。 组件将间接的接收props,而你必须确保一切正常。

我们先探讨如何在没有 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>
        );
    }
}

ProductList .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>
);

当然,这不是处理数据的最好方式,但我希望能够用它说明为什么 prop drilling 很差劲。 那么 Context API 是如何帮我们避免这种情况呢?

介绍Context Web Store

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


重构非常简单 —— 我们不必对组件的结构进行任何修改。但是我们确实需要创建一些新组件: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。


翻译:疯狂的技术
原文:https://www.toptal.com/react/... 

 

链接: https://www.fly63.com/article/detial/2066

如何优雅的设计 React 组件

如今的 Web 前端已被 React、Vue 和 Angular 三分天下,尽管现在的 jQuery 已不再那么流行,但 jQuery 的设计思想还是非常值得致敬和学习的,特别是 jQuery 的插件化。

React深度编程:受控组件与非受控组件

受控组件与非受控组件在官网与国内网上的资料都不多,有些人觉得它可有可不有,也不在意。这恰恰显示React的威力,满足不同规模大小的工程需求。

React框架学习_关于React两种构建应用方式选择

一般在传统模式下,我们构建前端项目很简单。就是下载各种js文件,如JQuery、Echart等,直接放置在html静态文件。Webpack则是JavaScript中比较知名的打包工具。这两个构建工具构成了React应用快速搭建的基础。

Gatsby.js_一款基于React.js静态站点生成工具

Gatsby能快速的使用 React 生态系统来生成静态网站,可以结合React Component、Markdown 和服务端渲染来完成静态网站生成让他更强大。

React创建组件的三种方式及其区别

React推出后,出于不同的原因先后出现三种定义react组件的方式,殊途同归;具体的三种方式:函数式定义的无状态组件、es5原生方式React.createClass定义的组件、es6形式的extends React.Component定义的组件

react生命周期详解_深入理解React生命周期

React主要思想是通过构建可复用组件来构建用户界面,每个组件都有自己的生命周期,它规定了组件的状态和方法需要在哪个阶段改变和执行。所谓组件就是有限状态机,,表示有限个状态以及在这些状态之间的转移和动作行为的模型。

React + Webpack 构建打包优化

React 相关的优化:使用 babel-react-optimize 对 React 代码进行优化,检查没有使用的库,去除 import 引用,按需打包所用的类库,比如 lodash 、echarts 等.Webpack 构建打包存在的问题两个方面:构建速度慢,打包后的文件体积过大

react router中页面传值的三种方法

这篇文章主要介绍React Router定义路由之后如何传值,有关React和React Router 。react router中页面传值的三种方法:props.params、query、state

react 高阶组件的 理解和应用

react 高阶组件简单的理解是:一个包装了另一个基础组件的组件。高阶组件的两种形式:属性代理(Props Proxy)、反向继承 (Inheritance Inversion)

react中的refs属性的使用方法

React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。这个特殊的属性允许你引用 render() 返回的相应的支撑实例( backing instance )。这样就可以确保在任何时间总是拿到正确的实例

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!