今天就是春节了,祝各位鸡年大吉,心想事成
感觉这两天错过了好几个亿
净往外赔钱了~马云爸爸才给了我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",name: "Baseball"},price: "$29.99",stocked: false,name: "Basketball"},{category: "Electronics",price: "$99.99",name: "iPod Touch"},price: "$399.99",name: "iPhone 5"},price: "$199.99",name: "Nexus 7"}
];
category是产品类别
price是产品价格
stocked是有无库存
name是产品名称
拆分UI视图
首先我们要做的第一项工作就是拆分UI
把它们拆解成一个个组件,组件中还有子组件
并且赋给它们名字
这个UI可以拆分成五个组件
- FilterableProductTable (黄色)
完整的产品表 - SearchBar (深蓝色)
用户输入部分 - ProductTable (绿色)
产品完整展示部分 - ProductCategoryRow (天蓝色)
产品类别标头 - ProductRow (红色)
产品信息
当然你也可以在细拆把ProductTable中的Name和Price拆出来
不过我觉得没必要搞得这么复杂
组件层次关系如下:
- FilterableProductTable
- SearchBar
- ProductTable
- ProductCategoryRow
- ProductRow
构建静态版本
静态的版本也就是页面最初应该显示的版本
构建它不需要复杂的想法,只是代码多了点
官网上的文档是这样说的
In simpler examples,it’s usually easier to go top-down,
@H_502_140@
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',name: 'Football'},{category: 'Sporting Goods',price: '$9.99',name: 'Baseball'},price: '$29.99',name: 'Basketball'},{category: 'Electronics',price: '$99.99',name: 'iPod Touch'},price: '$399.99',name: 'iPhone 5'},price: '$199.99',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',name: 'Nexus 7'} ]; var FilterableProductTable = React.createClass({ getInitialState: function(){ return { inStockOnly: false,filterText: '' } },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的组件化、单向数据流的特点