React和React-native在編程的思想上是完全一樣的蔓肯,所以要寫出好的RN代碼吱殉,學學React的思想很有必要递礼。本文是學習facebook官方文章《Thinking in React》的記錄岖圈,包括關(guān)鍵點的翻譯和自己的理解鹦牛。
英文原文地址
這片文章大部分是翻譯和簡化搞糕,小部分是自己理解。
先看看需求
假設(shè)我們需要實現(xiàn)這樣一個可過濾的商品列表
而且我們有這樣的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"}
];
將設(shè)計稿分解成Component層次
使用矩形線框來確定Component和子Component曼追。這里要遵循單一職責原則窍仰,一個Component只做一件事情。
- FilterableProductTable-橙色:根節(jié)點
- SearchBar-藍色:處理用戶輸入
- ProductTable-綠色:根據(jù)用戶輸入展現(xiàn)商品列表
- ProductCategoryRow-藍綠色:顯示分類名稱
- ProductRow-紅色:顯示單個商品信息
如下為樹狀結(jié)構(gòu)
FilterableProductTable
|- SearchBar
|- ProductTable
|- ProductCategoryRow
|- ProductRow
完成靜態(tài)頁面
var ProductCategoryRow = React.createClass({
render: function() {
return (<tr><th colSpan="2">{this.props.category}</th></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>
);
}
});
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>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
});
var SearchBar = React.createClass({
render: function() {
return (
<form>
<input type="text" placeholder="Search..." />
<p>
<input type="checkbox" />
{' '}
Only show products in stock
</p>
</form>
);
}
});
var FilterableProductTable = React.createClass({
render: function() {
return (
<div>
<SearchBar />
<ProductTable products={this.props.products} />
</div>
);
}
});
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'}
];
ReactDOM.render(
<FilterableProductTable products={PRODUCTS} />,
document.getElementById('container')
);
需要注意完成靜態(tài)頁面的時候不要使用state礼殊,因為state都是用于保存用戶交互結(jié)果的驹吮。而且每個組件都只有render()方法,根組件FilterableProductTable將通過props獲得數(shù)據(jù)模型晶伦,這種單向數(shù)據(jù)流使得React能很好的模塊化钥屈,響應(yīng)也很快。
state和props是React中的兩種數(shù)據(jù)模型坝辫,理解兩者的區(qū)別很重要篷就。簡單的理解props是外部傳遞進來的數(shù)據(jù),state是用戶和組件交互時產(chǎn)生的數(shù)據(jù)近忙。更加詳細的說明可以參考Facebook的另一篇官方文章
小而全的state
這個例子中的數(shù)據(jù)包括
- 商品列表
- 用戶輸入的搜索關(guān)鍵字
- 復選框的狀態(tài)
- 過濾后的商品列表
判斷數(shù)據(jù)是否為state的標準
- 外部傳入的不是state
- 不會變化的不是state
- 可以通過其他state和props計算得到的不是state
根據(jù)以上原則竭业,是state的為
- 用戶輸入的搜索關(guān)鍵字
- 復選框的狀態(tài)
state的作用域
var ProductCategoryRow = React.createClass({
render: function() {
return (<tr><th colSpan="2">{this.props.category}</th></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>
);
}
});
var ProductTable = React.createClass({
render: function() {
var rows = [];
var lastCategory = null;
this.props.products.forEach(function(product) {
if (product.name.indexOf(this.props.filterText) === -1 || (!product.stocked && this.props.inStockOnly)) {
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;
}.bind(this));
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
});
var SearchBar = React.createClass({
render: function() {
return (
<form>
<input type="text" placeholder="Search..." value={this.props.filterText} />
<p>
<input type="checkbox" checked={this.props.inStockOnly} />
{' '}
Only show products in stock
</p>
</form>
);
}
});
var FilterableProductTable = React.createClass({
getInitialState: function() {
return {
filterText: '',
inStockOnly: false
};
},
render: function() {
return (
<div>
<SearchBar
filterText={this.state.filterText}
inStockOnly={this.state.inStockOnly}
/>
<ProductTable
products={this.props.products}
filterText={this.state.filterText}
inStockOnly={this.state.inStockOnly}
/>
</div>
);
}
});
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'}
];
ReactDOM.render(
<FilterableProductTable products={PRODUCTS} />,
document.getElementById('container')
);
確定state的作用域就是要確定哪個Component應(yīng)該持有這個state,這里有幾個步驟及舍。
- 找到所有render()中會用到這個state的Component
- 找到一個Component包括包含了所有1中的Component
- 2中的Component或者它的上級Component持有這個state
- 如果你找不到一個有意義的Component持有這個state未辆,就在這些節(jié)點之上創(chuàng)造一個新的Component來持有這個state
在我們的例子中
- ProductTable需要根據(jù)狀態(tài)來確定顯示的列表項目,SearchBar也需要狀態(tài)來顯示搜索文本和復選框狀態(tài)
- 所以包含兩者的公共Component是FilterableProductTable
- FilterableProductTable持有state也是有明確意義的
所以我們在FilterableProductTable中持有state锯玛,然后將state作為props傳遞給ProductTable和SearchBar
反向數(shù)據(jù)流
ar ProductCategoryRow = React.createClass({
render: function() {
return (<tr><th colSpan="2">{this.props.category}</th></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>
);
}
});
var ProductTable = React.createClass({
render: function() {
var rows = [];
var lastCategory = null;
this.props.products.forEach(function(product) {
if (product.name.indexOf(this.props.filterText) === -1 || (!product.stocked && this.props.inStockOnly)) {
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;
}.bind(this));
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
});
var SearchBar = React.createClass({
handleChange: function() {
this.props.onUserInput(
this.refs.filterTextInput.value,
this.refs.inStockOnlyInput.checked
);
},
render: function() {
return (
<form>
<input
type="text"
placeholder="Search..."
value={this.props.filterText}
ref="filterTextInput"
onChange={this.handleChange}
/>
<p>
<input
type="checkbox"
checked={this.props.inStockOnly}
ref="inStockOnlyInput"
onChange={this.handleChange}
/>
{' '}
Only show products in stock
</p>
</form>
);
}
});
var FilterableProductTable = React.createClass({
getInitialState: function() {
return {
filterText: '',
inStockOnly: false
};
},
handleUserInput: function(filterText, inStockOnly) {
this.setState({
filterText: filterText,
inStockOnly: inStockOnly
});
},
render: function() {
return (
<div>
<SearchBar
filterText={this.state.filterText}
inStockOnly={this.state.inStockOnly}
onUserInput={this.handleUserInput}
/>
<ProductTable
products={this.props.products}
filterText={this.state.filterText}
inStockOnly={this.state.inStockOnly}
/>
</div>
);
}
});
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'}
];
ReactDOM.render(
<FilterableProductTable products={PRODUCTS} />,
document.getElementById('container')
);
這一部分在原文中稍微有些復雜咐柜。簡而言之:state在FilterableProductTable中兼蜈,但是真正的交互發(fā)生在SearchBar的字元素中,所以需要從子向父傳遞信息拙友。這里主要是通過回調(diào)機制實現(xiàn)的为狸。
- 由FilterableProductTable通過props.onUserInput傳一個回調(diào)函數(shù)handleUserInput給SearchBar。
- 在文本或者復選狀態(tài)發(fā)生變化時遗契,通過onChange指定調(diào)用SearchBar的handleChange
- handleChange通過refs獲取文本和復選框狀態(tài)辐棒,并執(zhí)行回調(diào)函數(shù)。refs的機制文中并沒有詳述牍蜂。
- handleUserInput中改變state漾根,并更新頁面