赵走x博客
网站访问量:151958
首页
书籍
软件
工具
古诗词
搜索
登录
49、Flux:理念、回顾Whinepad
48、lint、Flow、测试与复验:测试
47、lint、Flow、测试与复验:Flow
46、lint、Flow、测试与复验:ESLint
45、lint、Flow、测试与复验:package.json
44、构建实例应用:<Whinepad>
43、构建实例应用:应用配置
43、构建实例应用:<Excel>:改进的新版本
42、构建实例应用:组件:对话框
41、构建实例应用:组件:Actions
39、构建实例应用:表单:Form
38、构建实例应用:表单:<FormInput>“工厂组件”
37、构建实例应用:表单:Rating组件
36、构建实例应用:表单:Suggest
35、构建实例应用:Button组件
34、构建实例应用:组件
33、构建实例应用:Whinepad v.0.0.1
32、发布
31、开始构建
30、安装必备工具
29、为应用开发做准备:一个模板应用
28、JSX 和表单
27、JSX 和HTML 的区别
26、在JSX 中返回多个节点
25、展开属性
24、HTML 实体
23、JSX入门
22、Excel:一个出色的表格组件:下载表格数据
21、Excel:一个出色的表格组件:即时回放
20、Excel:一个出色的表格组件:搜索
19、Excel:一个出色的表格组件:编辑数据
18、Excel:一个出色的表格组件:排序
17、Excel:一个出色的表格组件
16、 PureRenderMixin
15、 性能优化:避免组件更新
14、 生命周期示例:使用子组件
13、组件生命周期示例:使用mixin
12、组件:生命周期方法
11、中途改变属性
10、从外部访问组件
9、在初始化state 时使用props:一种反模式
8、 props 与state
7、关于DOM 事件的说明
6、组件:带状态的文本框组件
5、组件的state
4、组件的propTypes
3、组件的属性
2、组件的基础
1、Hello World
50、Flux:Store
17、Excel:一个出色的表格组件
资源编号:76067
书籍
React快速上手开发
热度:82
到目前为止,你已经学会了如何创建React 自定义组件,使用普通的DOM 组件和自定义组件编写(渲染)界面,设置属性,维护状态,挂载组件的生命周期方法,以及通过避免不必要的重新渲染优化性能。 在本章里,我们将温故而知新,创建一个更有趣的组件——数据表格。这个组件有点像Microsoft Excel v.0.1.beta 版本的原型,你可以对数据表的内容进行编辑、排序、搜索(筛选),并以可下载的文件格式导出数据。
到目前为止,你已经学会了如何创建React 自定义组件,使用普通的DOM 组件和自定义组件编写(渲染)界面,设置属性,维护状态,挂载组件的生命周期方法,以及通过避免不必要的重新渲染优化性能。 在本章里,我们将温故而知新,创建一个更有趣的组件——数据表格。这个组件有点像Microsoft Excel v.0.1.beta 版本的原型,你可以对数据表的内容进行编辑、排序、搜索(筛选),并以可下载的文件格式导出数据。 # 1、构造数据 表格和数据是紧密联系的,因此这个表格组件(不如把它称作Excel 吧)需要接收一个数据数组和一个表头数组。为了方便测试,我们从维基百科(http://en.wikipedia.org/wiki/List_of_bestselling_books )抓取了一份畅销书列表: ``` var headers = [ "Book", "Author", "Language", "Published", "Sales" ]; var data = [ ["The Lord of the Rings", "J. R. R. Tolkien", "English", "1954–1955", "150 million"], ["Le Petit Prince (The Little Prince)", "Antoine de Saint-Exupéry", "French", "1943", "140 million"], ["Harry Potter and the Philosopher's Stone", "J. K. Rowling", "English", "1997", "107 million"], ["And Then There Were None", "Agatha Christie", "English", "1939", "100 million"], ["Dream of the Red Chamber", "Cao Xueqin", "Chinese", "1754–1791", "100 million"], ["The Hobbit", "J. R. R. Tolkien", "English", "1937", "100 million"], ["She: A History of Adventure", "H. Rider Haggard", "English", "1887", "100 million"], ]; ``` # 2、表头循环 第一步,我们只需要把表格头部显示出来。以下是一个大致骨架: ``` var Excel=React.createClass({ render:function () { return( React.DOM.table(null, React.DOM.thead(null, Reacr.DOM.tr(null, this.props.headers.map(function (title) { return React.DOM.th(null,title); }) ) )) ) } }); ``` 现在你已经有了一个可用的组件,具体用法如下: ``` ReactDOM.render( React.createElement(Excel,{ headers:headers, initialData:data }), document.getElementById("app") ); ``` 输出结果如图3-1 所示。  图3-1:渲染表头 值得注意的是,这里用到了数组类型的map() 方法,其用途是返回一个包含子节点组件的新数组。数组类型的map() 方法会把数组(在这个例子中是header 数组)中的每个元素都传递到回调函数中。在这里,回调函数创建一个新的
组件并将其作为函数返回值。这就是React 之美:你可以使用JavaScript 语法,并借助这门语言的全部特性去创建界面。 循环、条件判断等功能都可以在React 中使用,你无需学习另一门“模板”语言或语法即可创建界面。 在传递子节点给组件时,既可以传递一个单独的数组参数,也可以把每个子节点作为独立的参数传递。因此下列两种写法是等价的: ``` // 传递独立的参数 React.DOM.ul( null, React.DOM.li(null, 'one'), React.DOM.li(null, 'two') ); // 传递数组 React.DOM.ul( null, [ React.DOM.li(null, 'one'), React.DOM.li(null, 'two') ] ); ``` # 3、消除控制台的警告信息 图3-1 中的控制台截图显示了一个警告信息。这个信息是关于什么的?应该如何修复呢? 它的意思是:“数组或迭代器中的每个子节点应包含唯一的key 属性。请检查使用了
的顶层渲染调用。” 使用了
的顶层渲染调用?由于这个应用现在只有一个组件,不难推断出问题出现的地方,但在现实开发中,可能有许多组件都创建了
元素。Excel 仅仅是一个变量名,在React 外部被赋值给一个React 组件而已,因此React 并不知道这个组件的名字叫Excel。 你可以通过给组件声明一个displayName 属性来解决这个问题: ``` var Excel = React.createClass({ displayName: 'Excel', render: function() { // ... } }; ``` 现在React 可以识别问题出在哪里了,警告信息变为“Each child in an array should have a unique“ key” prop. Check the render method of `Excel`.”。这样调试就方便多了,但警告信息依然存在。为了修复这个问题,只需要根据警告去检查代码即可。现在你已经知道问题发生在哪个render() 函数中: ``` this.props.headers.map(function(title, idx) { return React.DOM.th({key: idx}, title); }) ``` 这个函数做了什么事情?传递给Array.prototype.map() 的回调函数被调用时会提供三个参数:元素的值、元素的索引值(0、1、2 等)和整个数组。你只需要把每个元素的索引值(idx)提供给React 作为key 属性即可。这个key 属性只需要在该数组中保持唯一,而不需要保证在整个React 应用中唯一。 现在key 的问题已经被修复了,加上一点CSS 进行美化,你就可以享受这个新组件的0.0.1 版本了。它看起来挺漂亮的,而且控制台警告已经消失了(如图3-2 所示)。  图3-2:渲染没有警告的表格组件 仅仅为了调试而添加displayName 的做法似乎有些麻烦,不过还有更方便的方法:使用JSX(会在第4 章讨论)就不需要再定义这个属性,名字会自动产生。 # 4、添加【td】 内容 既然已经完成了漂亮的表头部分,现在是时候添加表格内容了。表头中的内容是一个一维数组(单行),但是data 是二维的。因此你需要双重循环:外层循环遍历行,而内层要经过每一行中的每一个数据(单元格)。这可以通过前面提到的.map() 方法完成: ``` data.map(function(row) { return ( React.DOM.tr(null, row.map(function(cell) { return React.DOM.td(null, cell); }) ) ); }) ``` 此外,还需要考虑data 变量本身:数据从哪里来?如何更改数据? Excel 组件调用者应该可以传递初始的表格数据。但随后数据可能会发生变化,因为用户可能会对表格进行排序、编辑等操作。换句话说,组件的state 会发生变化。因此,我们使用this.state.data保持跟踪数据变化,使用this.props.initialData 进行组件初始化。目前完整的实现看起来类似于下面这样(结果如图3-3 所示): ``` var Excel=React.createClass({ getInitialState:function(){ return {data:this.props.initialData} }, render:function () { return( React.DOM.table(null, React.DOM.thead(null, React.DOM.tr(null, this.props.headers.map(function (title,idx) { return React.DOM.th({key:idx},title); }) ) ), React.DOM.tbody(null, this.state.data.map(function (row, idx) { return ( React.DOM.tr({key:idx}, row.map(function (cell, idx) { return React.DOM.td({key:idx},cell) }) ) ) }) ) ) ) } }); ```  图3-3:渲染整个表格 可以看到,重复{key: idx} 使得组件数组中的每个元素都拥有唯一的键名。虽然所有的.map() 循环都是从索引0 开始的,但是没有关系,因为key 只需要保证在当前循环中唯一,而不是在整个应用中唯一。 render() 函数现在已经有点复杂、难以理解了,特别是闭合括号} 和)。别担心,JSX 可以帮助你减轻痛苦! 前面的代码片段中省略了propTypes 属性(尽管这个属性是可选的,但是推荐使用)。该属性既可以用作数据验证,也可以为组件提供文档。具体到这个例子,我们要尽量减少用户提供垃圾数据到这个漂亮Excel 组件的可能性。React.PropTypes 提供了一个array 验证器,以确保属性总是一个数组。此外,它还提供了一个arrayOf 函数,以验证数组元素的具体类型。在这个例子中,我们让表头标题和表格数据都只接受字符串数组: ``` propTypes:{ headers:React.PropTypes.arrayOf(React.PropTypes.string), initialData: React.PropTypes.arrayOf( React.PropTypes.arrayOf(React.PropTypes.string) ) }, ``` 现在数据检查足够严格了! ### 如何改进该组件 对于一个普通的Excel 电子表格而言,只能接受字符串数据显得过于苛刻。作为练习,你可以允许其接受更多数据类型(React.PropTypes.any)并根据不同类型进行不同方式的渲染(例如,右对齐数字类型的数据)。