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

假設(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 GoodsElectronics呢?
它們也是每一個(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è)置。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容