React組件開發流程——利用React構建簡單的可檢索產品數據表

【本文源址:http://blog.csdn.net/q1056843325/article/details/54755521 轉載請添加該地址】

今天就是春節了,祝各位雞年大吉,心想事成
感覺這兩天錯過了好幾個億
淨往外賠錢了~馬雲爸爸纔給了我2.08. 心塞
不過咱也算是參加過一個兩億的項目了
昨晚發現博客還增加了30+訪問流量
沒想到除夕夜還有這麼多努力的人. 佩服 d===( ̄▽ ̄*)b
好了廢話不多說


使用React構建可搜索產品數據表
是React官網上的一個demo,它不是很難
不過卻能夠很好的映射React的開發流程
而且把我們常用的語法基本都涉及了
可以讓我們對於React有更進一步的理解
React官方傳送門:Thinking in React

UI與JSON

首先我們從我們的UI設計師那裏拿到了UI模擬圖和JSON-API

[
  {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"}
];

category是產品類別
price是產品價格
stocked是有無庫存
name是產品名稱

拆分UI視圖

首先我們要做的第一項工作就是拆分UI
把它們拆解成一個個組件,組件中還有子組件
並且賦給它們名字
這個UI可以拆分成五個組件

  1. FilterableProductTable (黃色)
    完整的產品表
  2. SearchBar (深藍色)
    用戶輸入部分
  3. ProductTable (綠色)
    產品完整展示部分
  4. ProductCategoryRow (天藍色)
    產品類別標頭
  5. ProductRow (紅色)
    產品信息

當然你也可以在細拆把ProductTable中的Name和Price拆出來
不過我覺得沒必要搞得這麼複雜
組件層次關係如下:

  • FilterableProductTable
    • SearchBar
    • ProductTable
      • ProductCategoryRow
      • ProductRow

構建靜態版本

靜態的版本也就是頁面最初應該顯示的版本
構建它不需要複雜的想法,只是代碼多了點
官網上的文檔是這樣說的

In simpler examples, it’s usually easier to go top-down,
and on larger projects, it’s easier to go bottom-up and write tests as you build

意思就是像我們這樣簡單的例子中
通常由頂級層次組件向下來寫更簡單一些
而在大型的項目中
最佳實踐是從下級層次往上寫並且要做必要的測試
那在這裏的demo我們就從FilterableProductTable組件開始寫

var 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'}
];
var FilterableProductTable = React.createClass({
    render: function(){
        return (
            <div>
                <SearchBar/>
                <ProductTable products={this.props.products}/>
            </div>
        )
    }
});
var SearchBar = React.createClass({
    render: function(){
        return (
            <div>
                <input type="text"/><br/>
                <input type="checkbox"/>Only show products in stock<br/><br/>
            </div>
        )
    }
});
var ProductTable = React.createClass({
    render: function(){
        var rows = [];
        var lastCategory = null;
        this.props.products.forEach(function(product){
            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>
                        <td><strong>Name</strong></td>
                        <td><strong>Price</strong></td>
                    </tr>
                </thead>
                <tbody>
                    {rows}
                </tbody>
            </table>
        )
    }
});
var ProductCategoryRow = React.createClass({
    render: function(){
        return (
            <tr>
                <td><strong>{this.props.category}</strong></td>  
            </tr>
        )
    }
});
var ProductRow = React.createClass({
    render: function(){
        var name = this.props.product.stocked ?
                      this.props.product.name :
                      <span style={{color:"red"}}>{this.props.product.name}</span>;
        return (
            <tr>
                <td>{name}</td>
                <td>{this.props.product.price}</td>                
            </tr>
        )
    }
});
ReactDom.render(
    <FilterableProductTable products={PRODUCTS}/>,
    document.getElementById('root')
)

很好的體現了React單向數據流的特點
將JSON數據交給父級
然後父級再講數據傳遞下去
這裏比較關鍵的代碼就是ProductTable中的這部分了

var rows = [];
var lastCategory = null;
this.props.products.forEach(function(product){
    if(product.category !== lastCategory){
        rows.push(<ProductCategoryRow category={product.category} key={product.category}/>);
    }
    rows.push(<ProductRow product={product} key={product.name}/>);
    lastCategory = product.category;
});

利用了數組的形式將靜態列表的組件項存儲下來
使用變量lastCategory,噹噹前遍歷的product中category與lastCategory不同時
就向數組中添加ProductCategoryRow組件

售罄產品過濾

我們先來考慮點擊那個複選框來重置是否顯示售罄產品過濾
很容易的就可以想到必須要設置一個狀態
這個狀態就是是否只顯示有庫存的產品(或者不顯示售罄產品)
然後爲複選框綁定change事件改變狀態

var FilterableProductTable = React.createClass({
    getInitialState: function(){
        return {
            inStockOnly: false
        }
    },//設置初始狀態:產品默認展示全部
    checkHandler: function(){
        this.setState({
            inStockOnly: !this.state.inStockOnly
        });
    },//複選框要綁定的事件處理函數:改變inStockOnly狀態
    render: function(){
        return (
            <div>
                <SearchBar checkHandler={this.checkHandler}/> //將事件處理函數作爲數據傳遞給子級
                <ProductTable products={this.props.products} inStockOnly={this.state.inStockOnly}/>
                //將狀態state作爲屬性props傳遞給子級
            </div>
        )
    }
});
var SearchBar = React.createClass({
    render: function(){
        return (
            <div>
                <input type="text"/><br/>
                <input type="checkbox" onChange={this.props.checkHandler}/>Only show products in stock<br/><br/>
                //爲複選框綁定change事件處理函數
            </div>
        )
    }
});
var ProductTable = React.createClass({
    render: function(){
        var rows = [];
        var lastCategory = null;
        var products = this.props.inStockOnly ?
                       this.props.products.filter(function(product){
                           return product.stocked;
                       }) : this.props.products;
        //通過判斷狀態inStockOnly來決定是否過濾products數組
        products.forEach(function(product){
            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>
                        <td><strong>Name</strong></td>
                        <td><strong>Price</strong></td>
                    </tr>
                </thead>
                <tbody>
                    {rows}
                </tbody>
            </table>
        )
    }
});

關鍵字過濾產品

進度已經過去一大半了
剩下一個問題就是我們在輸入框中輸入字符時
同樣需要對產品進行“過濾”
現在我們需要另外的狀態——輸入框字符改變
還需要一個事件處理函數來改變這個狀態

var FilterableProductTable = React.createClass({
    getInitialState: function(){
        return {
            inStockOnly: false,
            filterText: '' //表示輸入框中字符
        }
    },
    checkHandler: function(){
        this.setState({
            inStockOnly: !this.state.inStockOnly
        });
    },
    textHandler: function(text){
        this.setState({
            filterText: text
        });
    }, //輸入框字符改變狀態隨之改變,但是還不能獲取輸入框中字符,所以設置一個參數test
    render: function(){
        return (
            <div>
                <SearchBar checkHandler={this.checkHandler} textHandler={this.textHandler}/>
                //將事件處理函數作爲數據傳遞給SearchBar組件
                <ProductTable products={this.props.products} inStockOnly={this.state.inStockOnly} filterText={this.state.filterText}/>
                //將filterText狀態傳遞給ProductTable組件
            </div>
        )
    }
});
var SearchBar = React.createClass({ 
    Handler: function(){
        this.props.textHandler(this.refs.input.value);
    }, //爲了將獲取的輸入框內字符傳入textHandler作爲參數,外部包裝一個函數
    render: function(){
        return (
            <div>
                <input type="text" ref="input" onChange={this.Handler}/><br/>
                // 綁定change事件處理函數
                <input type="checkbox" onChange={this.props.checkHandler}/>Only show products in stock<br/><br/>
            </div>
        )
    }
});
var ProductTable = React.createClass({
    render: function(){
        var rows = [];
        var lastCategory = null;
        var products = this.props.products;
        var inStockOnly = this.props.inStockOnly;
        var filterText = this.props.filterText;
        products = inStockOnly ?
                   products.filter(function(product){
                       return product.stocked;
                   }) : products;
        products = filterText ?
                   products.filter(function(product){
                       return product.name.indexOf(filterText) !== -1;
                   }) : products;//通過字符限制一步過濾
        products.forEach(function(product){
            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>
                        <td><strong>Name</strong></td>
                        <td><strong>Price</strong></td>
                    </tr>
                </thead>
                <tbody>
                    {rows}
                </tbody>
            </table>
        )
    }
});

完整版本

完整的腳本代碼如下

var React = require('react');
var ReactDom = require('react-dom');
var 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'}
];
var FilterableProductTable = React.createClass({
    getInitialState: function(){
        return {
            inStockOnly: false,
            filterText: ''
        }
    },
    checkHandler: function(){
        this.setState({
            inStockOnly: !this.state.inStockOnly
        });
    },
    textHandler: function(text){
        this.setState({
            filterText: text
        });
    },
    render: function(){
        return (
            <div>
                <SearchBar checkHandler={this.checkHandler} textHandler={this.textHandler}/>
                <ProductTable products={this.props.products} inStockOnly={this.state.inStockOnly} filterText={this.state.filterText}/>
            </div>
        )
    }
});
var SearchBar = React.createClass({ 
    Handler: function(){
        this.props.textHandler(this.refs.input.value);
    },
    render: function(){
        return (
            <div>
                <input type="text" ref="input" onChange={this.Handler}/><br/>
                <input type="checkbox" onChange={this.props.checkHandler}/>Only show products in stock<br/><br/>
            </div>
        )
    }
});
var ProductTable = React.createClass({
    render: function(){
        var rows = [];
        var lastCategory = null;
        var products = this.props.products;
        var inStockOnly = this.props.inStockOnly;
        var filterText = this.props.filterText;
        products = inStockOnly ?
                   products.filter(function(product){
                       return product.stocked;
                   }) : products;
        products = filterText ?
                   products.filter(function(product){
                       return product.name.indexOf(filterText) !== -1;
                   }) : products;
        products.forEach(function(product){
            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>
                        <td><strong>Name</strong></td>
                        <td><strong>Price</strong></td>
                    </tr>
                </thead>
                <tbody>
                    {rows}
                </tbody>
            </table>
        )
    }
});
var ProductCategoryRow = React.createClass({
    render: function(){
        return (
            <tr>
                <td><strong>{this.props.category}</strong></td>  
            </tr>
        )
    }
});
var ProductRow = React.createClass({
    render: function(){
        var name = this.props.product.stocked ?
                      this.props.product.name :
                      <span style={{color:"red"}}>{this.props.product.name}</span>;
        return (
            <tr>
                <td>{name}</td>
                <td>{this.props.product.price}</td>                
            </tr>
        )
    }
});
ReactDom.render(
    <FilterableProductTable products={PRODUCTS}/>,
    document.getElementById('root')
)

UI視圖切分爲組件模塊
將JSON數據放入父級組件
再將數據props流入子級組件
設置狀態位,然後通過事件處理函數改變狀態state
state改變,觸發DOM的不斷渲染
這個例子詮釋了React的組件化、單向數據流的特點

==主頁傳送門==

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章