今天就是春节了,祝各位鸡年大吉,心想事成
感觉这两天错过了好几个亿
净往外赔钱了~马云爸爸才给了我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,
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的组件化、单向数据流的特点