React和ES6二--ES6的类和ES7的property initializer

前一篇的内容太简单了,会不会很失望。这次就来一个接近实际应用的例子,对应的React的组件也会更加复杂。这次开发一个购物车的页面在这页面中你会看到某个产品的信息,比如:图片名称和价格。另外,一个用户可以增加和减少该商品的数量

创建cart_item.html

<!DOCTYPE html>
<html>
<head lang="en">
    <Meta charset="UTF-8">
    <title>React and ES6 Part 2</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.2/css/foundation.min.css">
</head>
<body>
<div class="root"></div>
<script src="dist/bundle.js"></script>
</body>
</html>

我们使用了一个CDN来引用样式。添加个样式美化一下这个小应用的内容dev.root会用来展示React组件生成内容

CartItem组件

现在开发Cart组件。创建文件cart.jsx

import React from 'react';
import ReactDOM from 'react-dom';
import CartItem from './cartItem';

const order = {
    title: 'Fresh fruits package',image: 'http://images.all-free-download.com/images/graphiclarge/citrus_fruit_184416.jpg',initialQty: 3,price: 8
};

ReactDOM.render(
    <CartItem title={order.title} image={order.image} initialQty={order.initialQty} price={order.price} />,document.querySelector('.root') );

cart.jsx作为购物车功能的总容器。这里引入了reactreact-dom。之后引入了CartItem组件,这个组件会在稍后给出定义。最后,在ReactDom.render方法中把CartItem组件展示在class等于root的HTML元素中。

CartItem组件中的title、image、initialQty和price都是props的值。这些值会在后面CartItem组件的定义中用到。props是组件给子组件传递数据的一种方式。

CartItem组件初步定义

现在应该给出CartItem的定义了。否则整个web app都无法正常运行。CartItem组件可以展示数据,也可以相应用户增加、减少操作。

import React from 'react';

export default class CartItem extends React.Component {
    // 1
    constructor(props) {
        super(props);
        this.state = {
            qty: props.initialQty,total: 0
        };
    }
    // 2
    componentwillMount() {
        this.recalculatetotal();
    }
    // 3
    increaseQty() {
        this.setState({qty: this.state.qty + 1},this.recalculatetotal);
    }
    // 3
    decreaseQty() {
        let newQty = this.state.qty > 0 ? this.state.qty - 1 : 0;
        this.setState({qty: newQty},this.recalculatetotal);
    }
    // 3
    recalculatetotal() {
        this.setState({total: this.state.qty * this.props.price});
    }

    render() {
        return (
            // ...略... 
        );
    }
}

下面解释一下:
1. constructor是ES6中类的构造函数。整个构造函数需要一个参数props,其全部的值都在上文的代码中给出:title、image等。需要注意的是在构造函数中第一个调用的是super(props)this.state = ...一句中,使用props初始化了整个组件的state初值。
2. ·componentwillMount()方法是组件的生命周期方法之一。在组件即将在HTML页面中绘制的时候调用。在componentwillMount()中使用方法recalculatetotal()计算了商品的总价。
3. 这些是增加、减少商品数量方法。这些方法用户点击了增加、减少按钮之后被调用在这方法中,更新了
state中物品数量的值,并在setState方法的回调中使用了recalculatetotal()`方法

CartItem的render方法

上一节没有给出render方法的具体实现,这里给出:

import React from 'react';

export default class CartItem extends React.Component {
    // ...略...

    render() {
        return (
            <article className="row large-4"> <figure className="text-center"> <p> <img src={this.props.image} /> </p> <figcaption> <h2>{this.props.title}</h2> </figcaption> </figure> <p className="large-4 column"><strong>Quantity: {this.state.qty}</strong></p> <p className="large-4 column"> <button onClick={this.increaseQty.bind(this)} className="button success">+</button> <button onClick={this.decreaseQty.bind(this)} className="button alert">-</button> </p> <p className="large-4 column"><strong>Price per item:</strong> ${this.props.price}</p> <h3 className="large-12 column text-center"> Total: ${this.state.total} </h3> </article> ); } }

render()方法中,我们使用了JSX语法添加了各种“HTML”节点。这些节点和HTML标签很像,但是并不是HTML元素。

不用担心this.decreaseQty.bind(this)之类的用法。这些会在下一篇里详细解释。

React在ES6中的Default Props和Props类型

假设我们现在需要给CartItem添加一些认的props,并可以自动检查props的类型。

幸好这些在React里都有内置支持。所以,相关代码非常少。

CartItem类定义的下面直接添加下面的代码

CartItem.propTypes = {
    title: React.PropTypes.string.isrequired,price: React.PropTypes.number.isrequired,initialQty: React.PropTypes.number
};

CartItem.defaultProps = {
    title: "Indefined Product",price: 100,initialQty: 0
};

这时候如果你在cart.jsx文件中给CartItem的title值为数值的话,你就会在浏览器中看到警告。

在项目中使用ES7

你可能要问了ES6还没整明白的就开始用上ES7了?我要说的是我们往前看,使用一些已经在静态语言里有的特性并不会造成太大的问题。最关键的问题是Babel已经支持了。

首先安装必要的Babel模块:npm install --save-dev babel-preset-stage-0

然后在.babelrc文件添加这个preset的配置:

{
    "presets": ["es2015","react","stage-0"]
}

ES7属性初始化器和React组件的Default Props、Props Types

CartItem类里,添加如下代码

export default class CartItem extends React.Component {
    static propTypes = {
        title: React.PropTypes.string.isrequired,initialQty: React.PropTypes.number
    };
    static defaultProps = {
        title: 'Undefined Product',initialQty: 0
    };

    constructor(props) {
        super(props);
        this.state = {
            qty: props.initialQty,total: 0
        };
    }

// ...略...

这样的定义和前面一节在类定义后面再定义default props和props types是一样的效果。但是这样的定义是不是更加的接近静态语言类中定义属性的方式呢?当然之前的default props和props types的定义也可以删去了。

ES7的方式定义React组件的State

最后一步就是把initial state(state初始值)从constructor里拿出来放到property initializer(属性初始化器)里。代码如下:

export default class CartItem extends React.Component {
    state = {
        qty: this.props.initialQty,total: 0
    };

    constructor(props) {
        super(props);
        // this.state = {
        // qty: props.initialQty,
        // total: 0
        // };
    }

    //...略...
}

记得把原来constructor里的state代码删了。

总结

通过这篇你就可以熟悉了如何用ES6写React的组件,也熟悉了如何使用ES7的property initializer。

相关文章

react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接...
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc ...