React與ES6系列:
1. React與ES6(一)開篇介紹
2. React和ES6(二)ES6的類和ES7的property initializer
3. React與ES6(三)ES6類和方法綁定
4. React與ES6(四)ES6如何處理React mixins
前一篇的內容太簡單了,會不會很失望。這次就來一個接近實際應用的例子,對應的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作爲購物車功能的總容器。這裏引入了react
和react-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()計算了商品的總價。
state
3. 這些是增加、減少商品數量的方法。這些方法在用戶點擊了增加、減少按鈕之後被調用。在這些方法中,更新了中物品數量的值,並在
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,
price: React.PropTypes.number.isRequired,
initialQty: React.PropTypes.number
};
static defaultProps = {
title: 'Undefined Product',
price: 100,
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。