使用React的思想来构建应用对我在实际项目中以及帮助他人解决实际问题时起到了很大作用,所以我翻译此文来向那些正在或即将陷入React或React-Native深坑的同胞们表示慰问。网上已经有人翻译过,我想用更易读的语言翻译一次,这也是我首次如此一本正经的翻译技术文章给大众阅读,权当练习吧。
原文地址:https://facebook.github.io/react/docs/thinking-in-react.html
转载还请注明出处以及原文地址,出于对作者和译者劳动成果的尊重吧,谢谢了我的哥。
Thinking in React
作者:Pete Hunt 译者:Rex Rao (sohobloo)
我认为React是使用JavaScript构建高性能大型Web应用的首选方案,我们已经在Facebook和Instagram中广泛使用,哎哟,效果不错哟。
React的众多优势之一是——且看它如何让你能顺着思路构建应用。在此,我将引领你用React逐步构建出一个可搜索的商品列表应用。
从模型图开始
假设设计师已经为我们提供了API并可以返回模拟的JSON数据。容我小小鄙视一下这位美工,因为原型图长成这个挫样:
我们的API返回的模拟JSON数据长这样:
[ {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"} ];
第一步:拆分并构建界面的组件层次结构树
你应该做的第一件事是为你原型图所有组件和子组件画个边框、起个名。要是你跟设计师坐一起,找他们喝喝茶,说不定他们的Photoshop图层名恰巧可以用作你React组件的名字!(译者:我只能说,Too young too simple, sometimes naive!)
但你怎么知道如何拆分一个组件呢?这和你平时决定是不是要新建一个函数或者类的道理一样一样的。其中有个叫做单一职责原则的原理,也就是说理想状态下一个组件只做一件事,当他需要做更多,那就应该继续拆拆拆。
如果你经常向用户展示JSON数据,你会发现只要你的数据模型建得好,你的界面乃至你的组件架构也会完美的与之映射。因为界面和数据模型倾向于支持相同的信息架构,这让界面拆分工作变简单了,拆分出的一个组件只对应展示数据模型中的一种数据就行。
你看,咱这简单的应用有5种组件。我用斜体标示出了每个组件要展示的数据。
-
FilterableProductTable(橙色): 包含整个示例 -
SearchBar(蓝色): 接收用户输入(user input) -
ProductTable(绿色): 显示基于用户输入(user input)过滤的数据集 (data collection) -
ProductCategoryRow(青色): 显示分类( category)头 -
ProductRow(红色): 显示每一行商品(product)
看ProductTable你会发现表头(含"Name"和"Price"标签)并没有拆分成组件,这是出于一种存在争议的个人喜好而已啦。这个例子中,既然渲染数据集 (data collection)是ProductTable的职责,那就让它作为此组件的一部分好了。要是它再复杂一点的话(比如排序功能),那就另当别论独立成ProductTableHeader组件咯。
让我们把从原型图中定义的组件组合成层次结构树。如果一个组件出现在另一个组件中,那么这个组件就是它的子组件,so easy:
-
FilterableProductTableSearchBar-
ProductTableProductCategoryRowProductRow
第二步:用React做个静态版
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') );