如何用React創(chuàng)建一個真實(shí)項(xiàng)目

假設(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 GoodsElectronics呢蹋艺?
它們也是每一個類別的表頭,但是橫跨兩行黄刚。所以我們可以輕松寫出這樣的結(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è)置印蓖。

  1. 搜索框里用戶輸入的值
  2. 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)載請說明來源凿跳,蟹蟹......

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市疮方,隨后出現(xiàn)的幾起案子控嗜,更是在濱河造成了極大的恐慌,老刑警劉巖骡显,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疆栏,死亡現(xiàn)場離奇詭異曾掂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)壁顶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門珠洗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人若专,你說我怎么就攤上這事许蓖。” “怎么了调衰?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵膊爪,是天一觀的道長。 經(jīng)常有香客問我嚎莉,道長米酬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任趋箩,我火速辦了婚禮赃额,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘阁簸。我一直安慰自己爬早,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布启妹。 她就那樣靜靜地躺著筛严,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饶米。 梳的紋絲不亂的頭發(fā)上桨啃,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天,我揣著相機(jī)與錄音檬输,去河邊找鬼照瘾。 笑死,一個胖子當(dāng)著我的面吹牛丧慈,可吹牛的內(nèi)容都是我干的析命。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼逃默,長吁一口氣:“原來是場噩夢啊……” “哼鹃愤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起完域,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤软吐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吟税,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凹耙,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡姿现,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了肖抱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片备典。...
    茶點(diǎn)故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖虐沥,靈堂內(nèi)的尸體忽然破棺而出熊经,到底是詐尸還是另有隱情,我是刑警寧澤欲险,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站匹涮,受9級特大地震影響天试,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜然低,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一喜每、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧雳攘,春花似錦带兜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至喧兄,卻和暖如春无畔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吠冤。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工浑彰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拯辙。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓郭变,卻偏偏與公主長得像,于是被迫代替她去往敵國和親涯保。 傳聞我的和親對象是個殘疾皇子诉濒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容