假設(shè)我們要?jiǎng)?chuàng)建一個(gè)應(yīng)用,如何用react的思維進(jìn)行創(chuàng)建呢?本例是react官網(wǎng)的例子,主要是進(jìn)行更詳細(xì)的敘述和細(xì)節(jié)補(bǔ)充。
注:本文默認(rèn)您已經(jīng)清楚react的基本用法了,不清楚請(qǐng)點(diǎn)擊React官網(wǎng)學(xué)習(xí)哦。
我們要?jiǎng)?chuàng)建一個(gè)什么樣的應(yīng)用?
假設(shè)我們要?jiǎng)?chuàng)建一個(gè)商品列表的應(yīng)用。
界面如下(一般界面我們可以從設(shè)計(jì)師處拿到)

而我們從后端獲取的數(shù)據(jù)如下:
[
{category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
{category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
{category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
{category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
{category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
{category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
];
Step 1: Break The UI Into A Component Hierarchy
(把UI分成層級(jí)組件)
首先你可以根據(jù)UI圖形為每個(gè)component劃分區(qū)域(一個(gè)矩形之類),如果設(shè)計(jì)稿做得好,很可能每個(gè)圖層的名稱就是我們的組件名稱。

如何進(jìn)行分層呢?原則和我們創(chuàng)建一個(gè)函數(shù)和對(duì)象是一致的,就是“單一職責(zé)原則”。一個(gè)組件理論上應(yīng)該只做一件事。如果內(nèi)能還能在劃分,那么它應(yīng)該繼續(xù)細(xì)化。
由于您經(jīng)常向用戶顯示JSON數(shù)據(jù)模型,您會(huì)發(fā)現(xiàn),如果您的模型構(gòu)建正確,您的UI(以及您的組件結(jié)構(gòu))將很好地映射。 這是因?yàn)閁I和數(shù)據(jù)模型傾向于遵循相同的信息架構(gòu),這意味著將UI分成組件的工作往往是微不足道的。 只需將其分解成代表您的數(shù)據(jù)模型的一部分。
基于以上原則,我們把應(yīng)用分成以下組件。
- FilterableProductTable (橘色): contains the entirety of the example
- SearchBar (藍(lán)色): receives all user input
- ProductTable (綠色): displays and filters the data collection based on user input
- ProductCategoryRow (藍(lán)綠色): displays a heading for each category
- ProductRow (紅色): displays a row for each product
所以我們的組件層級(jí)結(jié)果如下
- FilterableProductTable
- SearchBar
- ProductTable
- -- ProductCategoryRow
- -- ProductRow
根據(jù)我們的結(jié)構(gòu)創(chuàng)建初級(jí)代碼
代碼如下
/**
* Created by zhengzehao on 2017/10/18.
*/
import React, {Component} from 'react'
import './myCss.css'
class FilterableProductTable extends Component {
render() {
return (<div>
<SearchBar/>
<ProductTable/>
</div>)
}
}
class SearchBar extends Component {
render() {
return (<h2>SearchBar</h2>)
}
}
class ProductTable extends Component {
render() {
return (<div>
<h2>
ProductTable
</h2>
<div>
<ProductCategoryRow/>
<ProductRow/>
</div>
</div>)
}
}
class ProductCategoryRow extends Component {
render() {
return (<div>ProductCategoryRow</div>)
}
}
class ProductRow extends Component {
render() {
return (<div>ProductRow</div>)
}
}
const PRODUCTS = [
{category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
{category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
{category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
{category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
{category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
{category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
export default FilterableProductTable
我們預(yù)覽看看結(jié)果怎么樣。

Step 2: Build A Static Version in React(用創(chuàng)建一個(gè)靜態(tài)版本)
雖然上面的基礎(chǔ)版本看起有點(diǎn)丑,但是確定了我們的組件和層級(jí)關(guān)系,接下來我們不考慮其交互關(guān)系,先創(chuàng)建一個(gè)靜態(tài)版本。
因?yàn)閯?chuàng)建這樣的版本可以可以根據(jù)各個(gè)模塊分別創(chuàng)建,而不需要考慮那么多的數(shù)據(jù)傳遞方式。
首先需要確定的是,既然是靜態(tài)版本,我們還不需要state,因?yàn)槭褂胹tate意味著我們有數(shù)據(jù)的交互,而我們只是創(chuàng)建靜態(tài)版本。
而我們?yōu)槭裁匆胮rops呢?因?yàn)閜rops是父組件傳遞給子組件的方式,既然我們確定了層級(jí)關(guān)系,在靜態(tài)版本我們也需要確定子組件的數(shù)據(jù)來源。
至于是自上而下(top-down),還是自下而上(bottom-up),取決于我們的結(jié)構(gòu),一般小應(yīng)用我們用自上而下是比較快的,而層級(jí)比較多而復(fù)雜的組件,我們采取自下而上是比較理想的。
FilterableProductTable的修改
對(duì)于這個(gè)最大的上層組件,我們確定的是關(guān)系如下:
<div>
<SearchBar/>
<ProductTable/>
</div>
但既然我們<ProductTable/>需要展示數(shù)據(jù),我們的來源就需要從頂層這里獲取。
因此我們的組件修改如下:
(只是添加了products的引用,簡單吧...)
class FilterableProductTable extends Component {
render() {
return (<div>
<SearchBar/>
<ProductTable products={PRODUCTS}/>
</div>)
}
}
SearchBar的修改
想想我們的SearchBar長什么樣?

一個(gè)input框和一個(gè)checkbox,然后是一段文字
所以很容易就創(chuàng)建以下結(jié)構(gòu)。
ps:我們先不考慮數(shù)據(jù)交互,先做靜態(tài)版本,所以這里先不與
products交互。
class SearchBar extends Component {
render() {
return (
<div>
<input type="text" placeholder="Search"/>
<p>
<input type="checkbox"/>{''}Only show products in stock
</p>
</div>
)
}
}
這是的p標(biāo)簽只是用以區(qū)分input和checkbox,或者說換行用的,可以自行替換.
ProductTable的修改

同樣,我們根據(jù)UI來看html應(yīng)該怎么寫。
首先是一個(gè)table,里面包著兩個(gè)heade(Name & Price),
Sporting Goods和Electronics呢?
它們也是每一個(gè)類別的表頭,但是橫跨兩行。所以我們可以輕松寫出這樣的結(jié)構(gòu)。
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<th cols={2}>{category}</th>
</tr>
<tr>
<td>{name}</td>
<td>{price}</td>
</tr>
</tbody>
</table>
這里,Name和Price是固定的,而后面的內(nèi)容是根據(jù)后臺(tái)數(shù)據(jù)變化的,所以可以把這部分設(shè)置為一個(gè)變量,如下:
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
這里我們的rows要怎么寫呢?
考慮一下,我們首先需要一個(gè)數(shù)組,包含所有的table內(nèi)容,每個(gè)類別有一個(gè)表頭和內(nèi)容,
所以應(yīng)該是這樣的
rows=[<Category1/>,<Row1/>,<Row2/>,
<Category2/>,<Row3/>,</Row4/>]
而我們的后臺(tái)數(shù)據(jù)是這樣的
const PRODUCTS = [
{category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
{category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
{category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
{category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
{category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
{category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
這么形成rows形式的結(jié)構(gòu)呢?就是對(duì)一個(gè)數(shù)組判斷,如果有一個(gè)category(比如Sporting Goods),就push這個(gè)表頭,然后push后面的內(nèi)容.
如果下一個(gè)對(duì)象的category還是一樣的,我們就忽略這個(gè)category,但是繼續(xù)push其他內(nèi)容。直到我們遇到下一個(gè)category(比如Electronics),我們才push這個(gè)表頭。
const rows = [];
let lastCategory = null;//作為是否是新category的判斷
//首先我們需要從父元素獲取這個(gè)products的對(duì)象列表,然后對(duì)每個(gè)元素進(jìn)行遍歷(category有新的就push表頭,否則就push內(nèi)容)
this.props.products.forEach((product) => {
//要清楚我們需要的結(jié)構(gòu)rows = [<Category1/>,<Row1/>,<Row2/>,<Category2/>,<Row3/>,</Row4/>]
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category}/>
);
//這里添加cataegory時(shí)因?yàn)樽釉匦枰@取每個(gè)表頭的名字,key就不多說了,不清楚可以看官網(wǎng)了解key的作用
}
rows.push(
<ProductRow
product={product}
key={product.name}/>
);
//這里添加product是因?yàn)樽釉匦枰@取每個(gè)product的內(nèi)容,包括價(jià)格,名稱等等
lastCategory = product.category;
});
所以我們創(chuàng)建了一個(gè)單項(xiàng)數(shù)據(jù)流的結(jié)構(gòu),自上而下,只有一個(gè)render()方法,當(dāng)我們需要確認(rèn)UI變化的原因,可以很容易地追溯到。
綜上,我們的ProductTable代碼如下:
class ProductTable extends React.Component {
render() {
const rows = [];
let lastCategory = null;
this.props.products.forEach((product) => {
//要清楚我們需要的結(jié)構(gòu)rows = [<Category1/>,<Row1/>,<Row2/>,<Category2/>,<Row3/>,</Row4/>]
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category}/>
);
}
rows.push(
<ProductRow
product={product}
key={product.name}/>
);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
}
ProductCategoryRow的修改
這里的html結(jié)構(gòu)我們上面已經(jīng)討論過了。
由于需要category的字段,我們直接從父元素獲取(this.props.category)
class ProductCategoryRow extends Component {
render() {
var category = this.props.category;
return (
<tr>
<th cols={2}>{category}</th>
</tr>
)
}
}
ProductRow的修改
結(jié)構(gòu)之前已經(jīng)討論過,而我們需要對(duì)是否stocked進(jìn)行標(biāo)記,所以采取
三元表示符。(stocked為false的顏色為紅)
const name = product.stocked ?
product.name :
<span style={{color: 'red'}}>
{product.name}
</span>;
class ProductRow extends React.Component {
render() {
const product = this.props.product;
const name = product.stocked ?
product.name :
<span style={{color: 'red'}}>
{product.name}
</span>;
// 如果是stocked就直接輸出,否則用span包裹并設(shè)置style的color為red
return (
<tr>
<td>{name}</td>
<td>{product.price}</td>
</tr>
);
}
}
完整的代碼
/**
* Created by zhengzehao on 2017/10/18.
*/
import React, {Component} from 'react'
class FilterableProductTable extends Component {
render() {
return (<div>
<SearchBar/>
<ProductTable products={PRODUCTS}/>
</div>)
}
}
class SearchBar extends Component {
render() {
return (
<div>
<input type="text" placeholder="Search"/>
<p>
<input type="checkbox"/>{''}Only show products in stock
</p>
</div>
)
}
}
class ProductTable extends React.Component {
render() {
const rows = [];
let lastCategory = null;
this.props.products.forEach((product) => {
//要清楚我們需要的結(jié)構(gòu)rows = [<Category1/>,<Row1/>,<Row2/>,<Category2/>,<Row3/>,</Row4/>]
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category}/>
);
}
rows.push(
<ProductRow
product={product}
key={product.name}/>
);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
}
class ProductCategoryRow extends Component {
render() {
var category = this.props.category;
return (<tr>
<th cols={2}>{category}</th>
</tr>)
}
}
class ProductRow extends React.Component {
render() {
const product = this.props.product;
const name = product.stocked ?
product.name :
<span style={{color: 'red'}}>
{product.name}
</span>;
// 如果是stocked就直接輸出,否則用span包裹并設(shè)置style的color為red
return (
<tr>
<td>{name}</td>
<td>{product.price}</td>
</tr>
);
}
}
const PRODUCTS = [
{category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
{category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
{category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
{category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
{category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
{category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
export default FilterableProductTable
預(yù)覽一下:

Step 3: Identify The Minimal (but complete) Representation Of UI State
要使UI產(chǎn)生交互效果,在REACT中我們是通過state來進(jìn)行的。
考慮一下我們的的各種數(shù)據(jù)有:
- 后端提供的products列表。
- input標(biāo)簽的輸入框產(chǎn)生的內(nèi)容
- checkbox的結(jié)果
- ProductTable過濾后的結(jié)果

考慮是不是state數(shù)據(jù)有以下原則
- 是否可以從父組件通過props傳遞過來?
- If so, it probably isn’t state.
- 是否是一個(gè)常量?
- If so, it probably isn’t state.
- 是否可以根據(jù)其他state或者props計(jì)算出來?
- If so, it isn’t state.
接下來逐項(xiàng)分析:
- products列表的內(nèi)容可以從父元素的props獲取,所以不是state。
- 搜索框和checkbox的內(nèi)容似乎是state,因?yàn)樗鼈儫o法從其他元素獲取或者計(jì)算得出。
- 最后,過濾的列表也不是state,因?yàn)樗梢酝ㄟ^搜索框和checkbox的值進(jìn)行篩選得出。
最終,我們只有兩個(gè)state需要設(shè)置。
- 搜索框里用戶輸入的值
- checkbox的值
Step 4:Identify Where Your State Should Live
好的,所以我們確定了最小的應(yīng)用狀態(tài)是什么。 接下來,我們需要確定哪個(gè)組件應(yīng)該改變或擁有這種狀態(tài)。
記?。篟eact是一個(gè)單向數(shù)據(jù)流的層級(jí)結(jié)構(gòu),而我們或許一開始并不清楚需要把state設(shè)置在哪里。
這通常是對(duì)入門者最困擾的地方,但是我們可以根據(jù)下面的步驟進(jìn)行分析。
對(duì)每個(gè)state進(jìn)行分析:
- Identify every component that renders something based on that state.
- 找出每個(gè)需要根據(jù)state進(jìn)行渲染的組件
- Find a common owner component (a single component above all the components that need the state in the hierarchy).
- 找出一個(gè)共通組件(一般是層級(jí)組件的上層)
- Either the common owner or another component higher up in the hierarchy should own the state.
- 一個(gè)共通組件或者另外的更高階層的組件應(yīng)該擁有這些state
- If you can’t find a component where it makes sense to own the state, create a new component simply for holding the state and add it somewhere in the hierarchy above the common owner component.
- 如果沒有找到一個(gè)組件適合去存放這些state,我們可以創(chuàng)建一個(gè)。
我們根據(jù)上面的原則進(jìn)行分析
ProductTable需要根據(jù)與用戶在搜索框的輸入和checkbox的勾選進(jìn)行過濾數(shù)據(jù),把過濾后的數(shù)據(jù)展示在上面。
共通的組件便是FilterableProductTable.(ProductTable和SearchBar的共通父級(jí))
Cool, so we’ve decided that our state lives in FilterableProductTable
① 在FilterableProductTable 建立初始statethis.state = {filterText: '', inStockOnly: false}
② 在<ProductTable/> 和 <SearchBar/>傳遞props filterText 和 inStockOnly=state對(duì)應(yīng)的值,使子組件獲取到該值。
<div>
<SearchBar filterText={this.state.filterText}inStockOnly={this.state.inStockOnly}/>
<ProductTable products={PRODUCTS}
filterText={this.state.filterText} inStockOnly={this.state.inStockOnly}/>
</div>
③ 用這些props去過濾ProductTable的內(nèi)容,并且把SeachBar對(duì)應(yīng)的props設(shè)為這些state。
class SearchBar extends Component {
render() {
const filterText = this.props.filterText;
const inStockOnly = this.props.inStockOnly;
return (
<div>
<input type="text" placeholder="Search" value={filterText}/>
<p>
<input type="checkbox" checked={inStockOnly}/>{''}Only show products in stock
</p>
</div>
)
}
}
class ProductTable extends React.Component {
render() {
const filterText = this.props.filterText;
const inStockOnly = this.props.inStockOnly;
const rows = [];
let lastCategory = null;
this.props.products.forEach((product) => {
// new
if (product.name.indexOf(filterText) === -1) {
return;
}
if (inStockOnly && !product.stocked) {
return;
}
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category}/>
);
}
rows.push(
<ProductRow
product={product}
key={product.name}/>
);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
}
完整的代碼
/**
* Created by zhengzehao on 2017/10/18.
*/
import React, {Component} from 'react'
class FilterableProductTable extends Component {
constructor(props) {
super(props)
this.state = {
filterText: '',
inStockOnly: false
};
}
render() {
return (<div>
<SearchBar filterText={this.state.filterText} inStockOnly={this.state.inStockOnly}/>
<ProductTable products={PRODUCTS}
filterText={this.state.filterText}
inStockOnly={this.state.inStockOnly}/>
</div>)
}
}
class SearchBar extends Component {
render() {
const filterText = this.props.filterText;
const inStockOnly = this.props.inStockOnly;
return (
<div>
<input type="text" placeholder="Search" value={filterText}/>
<p>
<input type="checkbox" checked={inStockOnly}/>{''}Only show products in stock
</p>
</div>
)
}
}
class ProductTable extends React.Component {
render() {
const filterText = this.props.filterText;
const inStockOnly = this.props.inStockOnly;
const rows = [];
let lastCategory = null;
this.props.products.forEach((product) => {
// new
if (product.name.indexOf(filterText) === -1) {
return;
}
if (inStockOnly && !product.stocked) {
return;
}
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category}/>
);
}
rows.push(
<ProductRow
product={product}
key={product.name}/>
);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
}
class ProductCategoryRow extends Component {
render() {
var category = this.props.category;
return (<tr>
<th cols={2}>{category}</th>
</tr>)
}
}
class ProductRow extends React.Component {
render() {
const product = this.props.product;
const name = product.stocked ?
product.name :
<span style={{color: 'red'}}>
{product.name}
</span>;
// 如果是stocked就直接輸出,否則用span包裹并設(shè)置style的color為red
return (
<tr>
<td>{name}</td>
<td>{product.price}</td>
</tr>
);
}
}
const PRODUCTS = [
{category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
{category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
{category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
{category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
{category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
{category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
export default FilterableProductTable
Step 5: Add Inverse Data Flow
到目前為止,我們已經(jīng)構(gòu)建了一個(gè)應(yīng)用程序,可以在層次結(jié)構(gòu)中用function的props或者state進(jìn)行正確地渲染。 現(xiàn)在是時(shí)候來進(jìn)行數(shù)據(jù)交互了。
React這樣的數(shù)據(jù)結(jié)構(gòu)使我們能明確地知道各個(gè)組件之間的數(shù)據(jù)流動(dòng),但相對(duì)一般的雙向數(shù)據(jù)流,我們需要多一步(通過回調(diào)函數(shù))來改變state的值。
如果你嘗試在上一步建立的app中進(jìn)行輸入或者改變checkbox的狀態(tài),你會(huì)發(fā)現(xiàn)是無效的。原因是我們?cè)O(shè)置了input的值永遠(yuǎn)是由FilterableProductTable的state進(jìn)行傳遞的。
讓我們確認(rèn)一下發(fā)生了什么。我們想要確定無論何時(shí),用戶改變表單,我們就根據(jù)輸入的內(nèi)容(包括input和checkbox)改變我們的state。
FilterableProductTable 會(huì)傳遞回調(diào)函數(shù),只要我們的state需要變更,它便會(huì)觸發(fā)。
我們可以通過onChange作為這個(gè)回調(diào)函數(shù),一旦用戶的表單發(fā)生改變,我們就用setState()來改變state。
完整代碼
相對(duì)上一步,我們就多了兩個(gè)函數(shù),onChange的時(shí)候調(diào)用這兩個(gè)函數(shù)進(jìn)行改變state,注意要綁定this哦??...
/**
* Created by zhengzehao on 2017/10/18.
*/
import React, {Component} from 'react'
class FilterableProductTable extends Component {
constructor(props) {
super(props)
this.state = {
filterText: '',
inStockOnly: false
};
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
this.handleInStockChange = this.handleInStockChange.bind(this);
}
handleFilterTextChange(filterText){
this.setState({
filterText: filterText
});
}
handleInStockChange(inStockOnly){
this.setState({
inStockOnly: inStockOnly
})
}
render() {
return (<div>
<SearchBar filterText={this.state.filterText} inStockOnly={this.state.inStockOnly}
onFilterTextChange={this.handleFilterTextChange}
onInStockChange={this.handleInStockChange}/>
<ProductTable products={PRODUCTS}
filterText={this.state.filterText}
inStockOnly={this.state.inStockOnly}/>
</div>)
}
}
class SearchBar extends Component {
constructor(props) {
super(props);
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
this.handleInStockChange = this.handleInStockChange.bind(this);
}
handleFilterTextChange(e) {
this.props.onFilterTextChange(e.target.value);
}
handleInStockChange(e) {
this.props.onInStockChange(e.target.checked);
}
render() {
const filterText = this.props.filterText;
const inStockOnly = this.props.inStockOnly;
return (
<div>
<input type="text" placeholder="Search" value={filterText} onChange={this.handleFilterTextChange}/>
<p>
<input type="checkbox" checked={inStockOnly} onChange={this.handleInStockChange}/>{''}Only show products in stock
</p>
</div>
)
}
}
class ProductTable extends React.Component {
render() {
const filterText = this.props.filterText;
const inStockOnly = this.props.inStockOnly;
const rows = [];
let lastCategory = null;
this.props.products.forEach((product) => {
// new
if (product.name.indexOf(filterText) === -1) {
return;
}
if (inStockOnly && !product.stocked) {
return;
}
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category}/>
);
}
rows.push(
<ProductRow
product={product}
key={product.name}/>
);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
}
class ProductCategoryRow extends Component {
render() {
var category = this.props.category;
return (
<tr>
<th cols={2}>{category}</th>
</tr>)
}
}
class ProductRow extends React.Component {
render() {
const product = this.props.product;
const name = product.stocked ?
product.name :
<span style={{color: 'red'}}>
{product.name}
</span>;
// 如果是stocked就直接輸出,否則用span包裹并設(shè)置style的color為red
return (
<tr>
<td>{name}</td>
<td>{product.price}</td>
</tr>
);
}
}
const PRODUCTS = [
{category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
{category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
{category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
{category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
{category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
{category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
export default FilterableProductTable
此時(shí)就可以進(jìn)行篩選和搜索了。


以上,轉(zhuǎn)載請(qǐng)說明來源,蟹蟹......