最近准备系统学习React,首先看了官方文档,React官方中文文档,读到快速开始的React理念这一章,就动手写了一下Demo,按照提示实现了一遍,效果如下:
Github地址:https://lucyzlu.github.io/React-demo-product-list/my-app/build/index.html
JSFiddle:
下面提取官方文档中重要的点复制到此处,以及动手的过程中遇到的一些问题记录,最后贴上完整的HTML,CSS以及React+JSX源码以供参考。
提供的效果图如下,这是一个商品列表,上面的部分是对商品进行过滤搜索的交互区域,下面部分是根据用户的输入进行过滤后的商品列表展示。
如何进行组件化呢?
官方提供了如下的组件化划分方案:
在这里你会看到,我们的简单应用中有5个组件。我们把每个组件展示的数据用斜体表示。
组件层级可以表示成:
看起来很明了,那么就动手实现吧。我选择从底层组件开始写,一直写到顶层组件。
整个步骤顺序:
第一步:把 UI 划分出组件层级
第二步:用 React 创建一个静态版本(也就是只在组件中只使用props进行单项数据流传递(不使用state),从父组件到子组件,并且在每个组件中使用render函数定义要渲染的UI,所有组件写好之后,为顶级组件提供一个数据看一下整个静态的UI是否正确)
第三步:定义 UI 状态的最小(但完整)表示(也就是确定有哪些state),考虑state时遵循如下3个问题:
第四步:确定你的 State 应该放在哪个组件中,遵循的4个问题如下:
第五步:添加反向数据流(也就是向props中添加事件处理程序,触发父组件传递给子组件用于修改父组件中state的回调函数,注意这里每个组件只能直接修改存储在自己内部的state,调用this.setState()函数进行修改,并不能直接修改自己上级或者下级组件的state:当要修改自己上级组件的state时,可以调用上级组件通过props传递给自己的回调函数;对下级组件的数据传递通过props)
实现的过程中遇到了两个问题,
1.JSX语法问题
一个是语法上面的问题,在定义ProductTable时使用了嵌套map函数,第一个map函数return的时候没有用一个顶级元素将其他元素包裹起来导致报错,因此:
map返回JSX的时候,要用一个顶级元素包裹起来,比如用tbody,否则会报错
一个table里可以有多个tbody标签
{this.props.categoryArray.map((item, index) => {
return (
"2">0].category} />
{
item.map((line) => {
if (line.name.toLowerCase().includes(this.props.filterText.toLowerCase())&&(this.props.onlyStocked?line.stocked:true)) {
return ( )
} else {
return null;
}
})
}
)
})}
2.回调函数的this绑定(这一点理解很重要)
如下,当引擎调用onChange事件指定的事件处理程序时,该回调函数内部的this默认是指向undefined的,因此调用this.props时就会报错,不能调用undefined的属性,
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(e) {
const target = e.target;
const value = target.type == "checkbox" ? target.checked : target.value;
target.type == "checkbox" ? this.props.onCheckedChange(value) : this.props.onValueChange(value);
}
render() {
return (
<div className="searchBar">
<div> "text" className="insetShadow" placeholder="Search..." value={this.props.search} value={this.props.search} onChange={this.handleInputChange} />div>
<div>"checkbox" name="filter" checked={this.props.isChecked} onChange={this.handleInputChange} /> div>
div>
);
}
}
解决办法之一是在constructor中使用bind函数显式绑定回调函数内的this。关于bind函数
3.数据结构问题
第三个问题是针对这个Demo的一个问题,就是展示商品列表的同时还要给同类的商品展示一个共同的类别名(也就是组件ProductCategoryRow),那么ProductCategoryRow要显示的Category名从何而来呢?观察一下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"}
];
我们可以将JSON数据进行改造,改成一个二维数组,二维数组的每个数组元素里的产品元素都具有相同的Category,那么展示的时候提取其第一个产品元素的Category就可以了,改造后的二维数据如下:
this.data = [
[{ 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" }]
];
github源码:https://github.com/LucyZlu/React-demo-product-list
React官方中文文档
bind应用场景和理解