5. 表格组件
1. 构造数据
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. 表头循环headers
var Excel = React.createClass({
render: function () {
return (
React.DOM.table(null,
React.DOM.thead(null,
React.DOM.tr(null,
this.props.headers.map(function (title, index) {
return React.DOM.th(null, title)
})))
)
)
}
})
ReactDOM.render(
React.createElement(Excel,{
headers:headers,
initialData: data
}),
document.getElementById("app")
)
在传递节点给组件时,既可以传递一个单独的数组参数,也可以把每个子节点作为独立的参数传递。上面map方法返回的就是子节点组成的数组。
3. 优化调试
当我们没加key值的时候,调试中会抛出:
Warning: Each child in an array or iterator should have a unique "key" prop. Check the top-level render call using
<tr>.
在开发中这样并不知道具体的报错组件,因为<tr>元素可能在很多地方用到,我们需要在组件中添加一条displayName
的属性:
var Excel = React.createClass({
displayName:'Excel'
})
这样报错就很具体了:
Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of
Excel.
displayName属性每次都要写觉得麻烦?在jsx中将会被自动产生赋值。
4. key的作用
上面的报错已经知道发生在Excel
的render()函数中,我们加上key:
this.props.headers.map(function (title,index) {
return React.DOM.th({ key: index }, title)
})
key属性只需要在该数组中保持唯一,不需要在整个react应用中唯一。
5. 添加表格内容
由于上面的data是二维的,因此需要双重循环:
data.map(function(row){
return(
React.DOM.tr(null,
row.map(function(cell){
return React.DOM.td(null,cell)
})
)
);
})
需要考虑的问题:data变量数据哪里来?随后数据可能会发生变化,由于是表格用户可能会对表格进行排序,编辑等操作。
组件的state会发生变化,因此我们使用this.state.data保持跟踪数据变化,使用this.props.initialData进行组件初始化
6. 表格排序
在点击表头时,根据点击的所在行进行排序。
先在表头添加点击事件属性。
React.DOM.thead({ onClick: this._sort },
....
),
_sort 方法
_sort: function (e) {
var column = e.target.cellIndex;
var data = this.state.data.slice(); //由于数据深层是基本属性,浅拷贝可用,拷贝的数据不影响state。
data.sort(function(a, b) {
console.log(a,b)
return a[column] > b[column] ? 1 : -1;
});
this.setState({
data: data,
});
},
不要尝试去直接改变state,请使用setState()进行操作。
7. 表格排序的视觉提示
给表头添加一个箭头符号。为了跟踪新的状态,需要添加两个新属性。
- this.state.sortby - 当前被选择的索引值。
- this.state.descending - 降序和升序的标记。
getInitialState:function(){
return {
data:this.props.initialData,
sortby:null,
descending:false
}
}
在_sort函数中,可以指定采用哪个方法进行排序。
_sort: function (e) {
var column = e.target.cellIndex;
var descending = this.state.sortby === column && !this.state.descending;
var data = this.state.data.slice(); //由于数据深层是基本属性,浅拷贝可用,拷贝的数据不影响state。
data.sort(function (a, b) {
return descending
? (a[column] < b[column] ? 1 : -1)
: (a[column] > b[column] ? 1 : -1);
});
this.setState({
data: data,
sortby: column,
descending: descending,
});
},
render: function () {
return (
React.DOM.table(null,
React.DOM.thead({onClick: this._sort},
React.DOM.tr(null,
this.props.headers.map(function(title, idx) {
if (this.state.sortby === idx) {
title += this.state.descending ? ' \u2191' : ' \u2193'
}
return React.DOM.th({key: idx}, title);
}, this) //map方法的第二个参数将作为回调方法的内部this指向,如果不传,this则为全局对象
)
),
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);
})
)
);
})
)
)
);
}

8. 表格可编辑数据
功能如下:
- 双击一个单元格,excel找出点击的单元格,并把表格内容从简单的文本变为一个填充了原内容的输入框。
- 编辑内容
- 回车键后,输入框消失,原内容被修改。
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"],
];
var Excel = React.createClass({
displayName: 'Excel',
getInitialState: function () {
return {
data: this.props.initialData,
sortby: null,
descending: false,
edit: null
};
},
_sort: function (e) {
var column = e.target.cellIndex;
var descending = this.state.sortby === column && !this.state.descending;
var data = this.state.data.slice(); //由于数据深层是基本属性,浅拷贝可用,拷贝的数据不影响state。
data.sort(function (a, b) {
return descending
? (a[column] < b[column] ? 1 : -1)
: (a[column] > b[column] ? 1 : -1);
});
this.setState({
data: data,
sortby: column,
descending: descending,
});
},
_dblclick: function (e) {
this.setState({
edit: {
cell: e.target.cellIndex,
row: parseInt(e.target.dataset.row, 10)
}
})
},
_submit: function (e) {
e.preventDefault();
var input = e.target.firstChild;
var data = this.state.data.slice();
data[this.state.edit.row][this.state.edit.cell] = input.value;
this.setState({
edit: null,
data: data,
});
},
componentWillMount() {
},
render: function () {
return (
React.DOM.table(null,
React.DOM.thead({ onClick: this._sort },
React.DOM.tr(null,
this.props.headers.map(function (title, idx) {
if (this.state.sortby === idx) {
title += this.state.descending ? ' \u2191' : ' \u2193'
}
return React.DOM.th({ key: idx }, title);
}, this)
)
),
React.DOM.tbody({ onDoubleClick: this._dblclick },
this.state.data.map(function (row, rowidx) {
return (
React.DOM.tr({
key: rowidx,
},
row.map(function (cell, cellidx) {
var content = cell;
var edit = this.state.edit;
if (edit && edit.row == rowidx && edit.cell == cellidx) {
content = React.DOM.form({ onSubmit: this._submit },
React.DOM.input(
{
type: 'text',
defaultValue: cell
}
)
)
}
return React.DOM.td({ key: cellidx, 'data-row': rowidx }, content);
}, this)
)
);
}, this)
)
)
);
}
})
ReactDOM.render(
React.createElement(Excel, {
headers: headers,
initialData: data
}),
document.getElementById("app")
)
如上,单元格只有一个输入框。