最近開始學習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來打包,希望大家一起學習