假設(shè)我們要創(chuàng)建一個應(yīng)用,如何用react的思維進(jìn)行創(chuàng)建呢揽浙?本例是react官網(wǎng)的例子鼠渺,主要是進(jìn)行更詳細(xì)的敘述和細(xì)節(jié)補(bǔ)充。
注:本文默認(rèn)您已經(jīng)清楚react的基本用法了你辣,不清楚請點(diǎn)擊React官網(wǎng)學(xué)習(xí)哦巡通。
我們要創(chuàng)建一個什么樣的應(yīng)用?
假設(shè)我們要創(chuàng)建一個商品列表的應(yīng)用舍哄。
界面如下(一般界面我們可以從設(shè)計師處拿到)
而我們從后端獲取的數(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ù)UI圖形為每個component劃分區(qū)域(一個矩形之類)宴凉,如果設(shè)計稿做得好,很可能每個圖層的名稱就是我們的組件名稱表悬。
如何進(jìn)行分層呢弥锄?原則和我們創(chuàng)建一個函數(shù)和對象是一致的,就是“單一職責(zé)原則”。一個組件理論上應(yīng)該只做一件事籽暇。如果內(nèi)能還能在劃分温治,那么它應(yīng)該繼續(xù)細(xì)化。
由于您經(jīng)常向用戶顯示JSON數(shù)據(jù)模型图仓,您會發(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
所以我們的組件層級結(jié)果如下
- FilterableProductTable
- SearchBar
- ProductTable
- -- ProductCategoryRow
- -- ProductRow
根據(jù)我們的結(jié)構(gòu)創(chuàng)建初級代碼
代碼如下
/**
* 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)建一個靜態(tài)版本)
雖然上面的基礎(chǔ)版本看起有點(diǎn)丑本今,但是確定了我們的組件和層級關(guān)系,接下來我們不考慮其交互關(guān)系主巍,先創(chuàng)建一個靜態(tài)版本冠息。
因?yàn)閯?chuàng)建這樣的版本可以可以根據(jù)各個模塊分別創(chuàng)建,而不需要考慮那么多的數(shù)據(jù)傳遞方式孕索。
首先需要確定的是逛艰,既然是靜態(tài)版本,我們還不需要state搞旭,因?yàn)槭褂胹tate意味著我們有數(shù)據(jù)的交互散怖,而我們只是創(chuàng)建靜態(tài)版本。
而我們?yōu)槭裁匆胮rops呢肄渗?因?yàn)閜rops是父組件傳遞給子組件的方式镇眷,既然我們確定了層級關(guān)系,在靜態(tài)版本我們也需要確定子組件的數(shù)據(jù)來源翎嫡。
至于是自上而下(top-down)欠动,還是自下而上(bottom-up),取決于我們的結(jié)構(gòu)钝的,一般小應(yīng)用我們用自上而下是比較快的翁垂,而層級比較多而復(fù)雜的組件,我們采取自下而上是比較理想的硝桩。
FilterableProductTable的修改
對于這個最大的上層組件,我們確定的是關(guān)系如下:
<div>
<SearchBar/>
<ProductTable/>
</div>
但既然我們<ProductTable/>
需要展示數(shù)據(jù)枚荣,我們的來源就需要從頂層這里獲取碗脊。
因此我們的組件修改如下:
(只是添加了products的引用,簡單吧...)
class FilterableProductTable extends Component {
render() {
return (<div>
<SearchBar/>
<ProductTable products={PRODUCTS}/>
</div>)
}
}
SearchBar的修改
想想我們的SearchBar長什么樣?
一個input框和一個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)該怎么寫。
首先是一個table丸逸,里面包著兩個heade(Name & Price),
Sporting Goods和Electronics呢蹋艺?
它們也是每一個類別的表頭,但是橫跨兩行黄刚。所以我們可以輕松寫出這樣的結(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ù)后臺數(shù)據(jù)變化的涛救,所以可以把這部分設(shè)置為一個變量,如下:
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
這里我們的rows要怎么寫呢业扒?
考慮一下检吆,我們首先需要一個數(shù)組,包含所有的table內(nèi)容凶赁,每個類別有一個表頭和內(nèi)容咧栗,
所以應(yīng)該是這樣的
rows=[<Category1/>,<Row1/>,<Row2/>,
<Category2/>,<Row3/>,</Row4/>]
而我們的后臺數(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)呢?就是對一個數(shù)組判斷虱肄,如果有一個category(比如Sporting Goods)致板,就push這個表頭,然后push后面的內(nèi)容.
如果下一個對象的category還是一樣的咏窿,我們就忽略這個category斟或,但是繼續(xù)push其他內(nèi)容。直到我們遇到下一個category(比如Electronics)集嵌,我們才push這個表頭萝挤。
const rows = [];
let lastCategory = null;//作為是否是新category的判斷
//首先我們需要從父元素獲取這個products的對象列表,然后對每個元素進(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時因?yàn)樽釉匦枰@取每個表頭的名字怜珍,key就不多說了,不清楚可以看官網(wǎng)了解key的作用
}
rows.push(
<ProductRow
product={product}
key={product.name}/>
);
//這里添加product是因?yàn)樽釉匦枰@取每個product的內(nèi)容凤粗,包括價格酥泛,名稱等等
lastCategory = product.category;
});
所以我們創(chuàng)建了一個單項(xiàng)數(shù)據(jù)流的結(jié)構(gòu),自上而下,只有一個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)討論過腥例,而我們需要對是否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.
- 是否是一個常量捐凭?
- If so, it probably isn’t state.
- 是否可以根據(jù)其他state或者props計算出來拨扶?
- If so, it isn’t state.
接下來逐項(xiàng)分析:
- products列表的內(nèi)容可以從父元素的props獲取,所以不是state茁肠。
- 搜索框和checkbox的內(nèi)容似乎是state患民,因?yàn)樗鼈儫o法從其他元素獲取或者計算得出。
- 最后垦梆,過濾的列表也不是state匹颤,因?yàn)樗梢酝ㄟ^搜索框和checkbox的值進(jìn)行篩選得出。
最終托猩,我們只有兩個state需要設(shè)置印蓖。
- 搜索框里用戶輸入的值
- checkbox的值
Step 4:Identify Where Your State Should Live
好的,所以我們確定了最小的應(yīng)用狀態(tài)是什么京腥。 接下來赦肃,我們需要確定哪個組件應(yīng)該改變或擁有這種狀態(tài)。
記坠恕:React是一個單向數(shù)據(jù)流的層級結(jié)構(gòu)他宛,而我們或許一開始并不清楚需要把state設(shè)置在哪里。
這通常是對入門者最困擾的地方欠气,但是我們可以根據(jù)下面的步驟進(jìn)行分析厅各。
對每個state進(jìn)行分析:
- Identify every component that renders something based on that state.
- 找出每個需要根據(jù)state進(jìn)行渲染的組件
- Find a common owner component (a single component above all the components that need the state in the hierarchy).
- 找出一個共通組件(一般是層級組件的上層)
- Either the common owner or another component higher up in the hierarchy should own the state.
- 一個共通組件或者另外的更高階層的組件應(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.
- 如果沒有找到一個組件適合去存放這些state,我們可以創(chuàng)建一個预柒。
我們根據(jù)上面的原則進(jìn)行分析
ProductTable需要根據(jù)與用戶在搜索框的輸入和checkbox的勾選進(jìn)行過濾數(shù)據(jù)讯检,把過濾后的數(shù)據(jù)展示在上面琐鲁。
共通的組件便是FilterableProductTable.(ProductTable和SearchBar的共通父級)
Cool, so we’ve decided that our state lives in FilterableProductTable
① 在FilterableProductTable 建立初始statethis.state = {filterText: '', inStockOnly: false}
② 在<ProductTable/> 和 <SearchBar/>傳遞props filterText 和 inStockOnly=state對應(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對應(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)建了一個應(yīng)用程序适贸,可以在層次結(jié)構(gòu)中用function的props或者state進(jìn)行正確地渲染灸芳。 現(xiàn)在是時候來進(jìn)行數(shù)據(jù)交互了。
React這樣的數(shù)據(jù)結(jié)構(gòu)使我們能明確地知道各個組件之間的數(shù)據(jù)流動拜姿,但相對一般的雙向數(shù)據(jù)流烙样,我們需要多一步(通過回調(diào)函數(shù))來改變state的值。
如果你嘗試在上一步建立的app中進(jìn)行輸入或者改變checkbox的狀態(tài)蕊肥,你會發(fā)現(xiàn)是無效的谒获。原因是我們設(shè)置了input的值永遠(yuǎn)是由FilterableProductTable的state進(jìn)行傳遞的。
讓我們確認(rèn)一下發(fā)生了什么壁却。我們想要確定無論何時批狱,用戶改變表單,我們就根據(jù)輸入的內(nèi)容(包括input和checkbox)改變我們的state展东。
FilterableProductTable 會傳遞回調(diào)函數(shù)赔硫,只要我們的state需要變更,它便會觸發(fā)盐肃。
我們可以通過onChange作為這個回調(diào)函數(shù)爪膊,一旦用戶的表單發(fā)生改變,我們就用setState()來改變state砸王。
完整代碼
相對上一步推盛,我們就多了兩個函數(shù),onChange的時候調(diào)用這兩個函數(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
此時就可以進(jìn)行篩選和搜索了。
以上荷辕,轉(zhuǎn)載請說明來源凿跳,蟹蟹......