跟著官網的例子學Reacjs (一)FilterableProductTable

橙子瓣發表於2016-07-15

  最近開始學習React,發現最好的方法不是看這個書那個書,而是直接上官網,一步步的跟著學習,真的獲益匪淺。許多翻譯的書上漏掉的知識點都可以學到。

入門的一些準備工作可以參照官網的步驟,引入依賴的核心包,就不多說了,直接上官網的第二個具體例子,一個過濾產品列表的Table:https://facebook.github.io/react/docs/thinking-in-react.html

     這篇文章主要介紹的並不是API的一些使用,而是向剛入門的同學介紹使用React設計元件時的一些思路,思考怎麼樣來更好的使用React。

  1. 首先,要做的是這麼個東西

一個很簡單的table列表,帶了一個搜尋功能,只是介面比較簡陋。。。(作者自己也調侃說這肯定是個糟糕的設計,哈哈)。鑑於此,使用bootstrap稍微規整了一下

  2. 接著,作者分析了這個元件該怎麼劃分:要本著最小原則,實現單一功能得原則,劃分了五個元件

每種顏色代表一個元件,最後元件的關係如下,很好理解:

FilterableProductTable

  • SearchBar
  • ProductTable
    • ProductCategoryRow
    • ProductRow

下面直接進入主題,來code這五個元件

  2.1 先來個最簡單的,SearchBar

  這個元件和其他的元件相對獨立,而且就一個input框和一個核取方塊

 1 var SearchBar = React.createClass({
 2     render: function () {
 3         return (
 4             <form>
 5                 <input type="text" placeholder="input product name"/><br/>
 6                 <input id="FPT_SearchBar_in_stock" type="checkbox"/>
 7                 <label htmlFor="FPT_SearchBar_in_stock">only show product in stock</label>
 8             </form>
 9         );
10     }
11 });

render方法只渲染了一個form,裡面一個倆input,搞定

  2.2 ProductCategoryRow

產品分類元件,也相對簡單些,只需要接受父元件的一個分類的名稱就可以了

1 var ProductCategoryRow = React.createClass({
2     render:function(){
3         return (
4             <tr>
5                 <th colSpan="2">{this.props.category}</th>
6             </tr>
7         );
8     }
9 });

  2.3 ProductRow

產品明細元件,接受父類的產品資訊,並且判斷,如果沒有庫存了,產品名字要顯示為紅色

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>
         );
    }
});

  2.4 ProductTable

產品列表元件負責渲染所有的產品資訊,通過接收父類穿進來的products屬性,遍歷迴圈,並負責給兩個子控制元件ProductCategoryRow 和 ProductRow 賦值

 1 var ProductTable = React.createClass({
 2     //根據資料獲得需要render的rows
 3     formatProducts: function () {
 4         var result = [];
 5         var category = "";
 6         this.props.products.forEach((product)=> {
 7             if(category !== product.category){
 8                 result.push(<ProductCategoryRow category={product.category} key={product.category}/>);
 9             }
10             result.push(<ProductRow product={product} key={product.name}/>);
11             category = product.category;
12         });
13         return result;
14     },
15     render: function () {
16         var products = this.formatProducts();
17         return (
18             <table className="table table-bordered">
19                 <thead>
20                     <tr>
21                         <th>
22                             Name
23                         </th>
24                         <th>
25                             Price
26                         </th>
27                     </tr>
28                 </thead>
29                 <tbody>
30                     {products}
31                 </tbody>
32             </table>
33         );
34     }
35 });

  2.5 最後一個元件FilterableProductTable

這個元件只負責給ProductTable的products屬性賦值就可以了,然後把SearchBar元件也包含進來。

 1 var FilterableProductTable = React.createClass({
 2     render: function () {
 3         return (
 4             <div>
 5                 <SearchBar />
 6                 <ProductTable products={this.state.data}/>
 7             </div>
 8         );
 9     },
10     getInitialState: function () {
11         return {
12             data: [],
13             filterName: '',
14             onlyStock: ''
15         };
16     },
17     //元件顯示後(生命週期中的方法)
18     componentDidMount: function () {
19         var defaultData = [
20             {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
21             {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
22             {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
23             {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
24             {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
25             {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
26         ];
27         setTimeout(function () {
28             this.setState({data: defaultData});
29         }.bind(this), 2000);
30     },
31     addOneProduct: function () {
32         var oldData = this.state.data;
33         var newData = {category: "Electronics", price: "$123", stocked: true, name: (new Date) - 0};
34         oldData.push(newData);
35         this.setState({data: oldData});
36     }
37 });

這裡做了一些處理,就是每個元件都有一個生命週期,其中內建的函式componentDidMount函式負責在元件顯示後呼叫,延遲2s給data傳遞了本地資料,這樣就有一個過了兩秒資料才載入出來的感覺

 

通過上面的兩步,頁面一個純靜態的樣子就出來了,過濾的功能還沒有加。

然後作者提到了一個關鍵的概念,就是React中的props和state,他倆的區別在哪?什麼時候使用state?

作者給出了一個解決的方法:

 讓我們通過每一個問題,找出哪一個是state。簡單地詢問每一塊資料三個問題:

它是從通過父元件的props傳遞?如果是這樣,它可能不是state。
是否隨著時間的推移保持不變?如果是這樣,它可能不是state。
你可以基於元件中的任何其他state或props計算它?如果是這樣,它不是state。

大概的翻譯了一下原文,如上。這應該就是使用state的一個原則吧。

接下來,開始實現過濾的功能之前,根據上面的原則,我們思考我們要實現的這幾個元件中,到底有哪些地方要用到state?

沒錯,一共3個:

1、整個表格的資料來源,正常情況下是從伺服器請求的,所以使用state

2、SearchBar中的搜尋框的輸入值,因為是使用者輸入的,時刻在發生變化

3、SearchBar中的核取方塊

 

  3、按照上面整理的,需要修改之前的兩個元件

 1 var FilterableProductTable = React.createClass({
 2     render: function () {
 3         return (
 4             <div>
 5                 <SearchBar filterName={this.state.filterName} onlyStock={this.state.onlyStock} onSearchChange={this.handleSearchChange}/>
 6                 <ProductTable products={this.state.data} filterName={this.state.filterName} onlyStock={this.state.onlyStock}/>
 7             </div>
 8         );
 9     },
10     getInitialState: function () {
11         return {
12             data: [],
13             filterName: '',
14             onlyStock: false
15         };
16     },
17     //元件顯示後(生命週期中的方法)
18     componentDidMount: function () {
19         var defaultData = [
20             {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
21             {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
22             {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
23             {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
24             {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
25             {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
26         ];
27         setTimeout(function () {
28             this.setState({data: defaultData});
29         }.bind(this), 2000);
30     },
31     //當搜尋框變化時的處理函式
32     handleSearchChange:function(filterName, onlyStock){
33         this.setState({
34             filterName: filterName,
35             onlyStock: onlyStock
36         });
37     },
38     addOneProduct: function () {
39         var oldData = this.state.data;
40         var newData = {category: "Electronics", price: "$123", stocked: true, name: (new Date) - 0};
41         oldData.push(newData);
42         this.setState({data: oldData});
43     }
44 });
 1 var SearchBar = React.createClass({
 2     render: function () {
 3         return (
 4             <form>
 5                 <input type="text" placeholder="input product name"
 6                        ref="filterTextInput"
 7                        value={this.props.filterName}
 8                        onChange={this.handleChange} /><br/>
 9                 <input id="FPT_SearchBar_in_stock" type="checkbox"
10                        ref="inputOnlyStock"
11                        checked={this.props.onlyStock}
12                        onChange={this.handleChange} />
13                 <label htmlFor="FPT_SearchBar_in_stock">only show product in stock</label>
14             </form>
15         );
16     },
17     handleChange:function(){
18         this.props.onSearchChange(this.refs.filterTextInput.value, this.refs.inputOnlyStock.checked);
19     }
20 });

OK,完成。

完整程式碼可以到我的github上下載執行:https://github.com/lyc-chengzi/reactProject

後續新的例子會繼續往上整合,並且用webpack來打包,希望大家一起學習

 

相關文章