在上篇文章中,我们使用React实现了一个TodoList,可以显示基本的待办事项的列表,今天我们继续添加一些功能,比如选中一个TodoItem的checkBox进而可以改变它的完成状态,添加一个搜索框,在搜索框中输入关键字可以对多条数据进行过滤。
我们还是在原来的基础上做改动,下面是最新的TodoList模块:
- var TodoList = React.createClass({
- getInitialState: function() {
- return {
- data: []
- };
- },componentDidMount: function() {
- var mockData = [
- {id: 1,name: "report the updates to Boss",time: "9:30"},{id: 2,name: "Stand-up meeting",time: "10:00"},{id: 3,name: "Draw up a plan for next step",time: "11:00"}
- ];
-
- this.setState({
- data: mockData
- });
- },render: function() {
- var todoItems = this.state.data.map(function(todo) {
- return (
- //passing the whole todo object as a property
- <TodoItem key={todo.id} todo={todo}/>
- );
- });
-
- return (
- <div className="todoList">{todoItems}</div>
- );
- }
- });
在上面的代码中,我们改进了TodoItem属性的传递方式,直接把一个todo数据对象作为属性提供给TodoItem,这样更利于开发过程中的数据处理。接下来,我们也要对TodoItem模块进行改进,代码如下:
- var TodoItem = React.createClass({
- //will be called after clicking the checkBox
- handleClick: function(event) {
- var todoData = this.props.todo;
-
- todoData.hasDone = !todoData.hasDone;
-
- //re-render the view
- this.setState({
- hasDone: todoData.hasDone
- });
-
- //Ajax handling maybe
- //updateTodo(this.props.todo);
- },getInitialState: function() {
- return {
- hasDone: false
- };
- },componentDidMount: function() {
- this.setState({
- hasDone: this.props.todo.hasDone
- });
- },render: function() {
- var classList = ['todoItem'];
- this.state.hasDone && classList.push('hasDone'); //add a 'hasDone' class after checkBox is checked
- var classNames = classList.join(' ');
-
- return (
- //adding 'onClick' property on checkBox to bind a click handler
- <div className={classNames}>
- <input type="checkBox" onClick={this.handleClick} checked={this.props.todo.hasDone}/>
- <div className="name">{this.props.todo.name}</div>
- <div className="time">{this.props.todo.time}</div>
- </div>
- );
- }
- });
这一部分改动的较多,我们仔细来分析每个部分的功能。
首先是getInitialState
部分,我们添加了初始状态,hasDone
表示一个待办事项是否已经完成,初始值为false
,然后在componentDidMount
函数中,我们根据上层传递进来的数据重新设置状态,这个状态最终会在render方法中起作用。
在render
方法中我们会首先判断这个Todo是否已经完成,如果已完成,就为其添加一个hasDone的className,最终会在页面中显示不同的样式。然后我们为checkBox声明了checked属性,它会根据数据中的hasDone来显示选中状态,这就意味着如果一个Todo是已完成的,页面初始化时这个checkBox是被选中的。
最后,我们为checkBox绑定一个点击事件,当它被点击时,handleClick函数就会被触发,在这个函数中,我们会获取最新的hasDone值,然后重置状态更新视图,实际开发中我们还需要做一些Ajax操作请求服务器更改数据库中的值。
最后,我们添加一个hasDone对应的样式:
- .todoItem.hasDone > div { text-decoration: line-through; }
现在我们来看一下最终的页面效果,如图所示,当我们点击第一个TodoItem后是这个样子的:
我们已经成功地添加了点击事件,下面我们还需要为这个TodoList添加一个过滤的功能,先创建一个SearchBox模块:
然后声明对应的CSS样式:
最后还需在TodoList模块的render方法中把SearchBox添加进去:
- render: function() {
- var todoItems = this.state.data.map(function(todo) {
- return (
- <TodoItem key={todo.id} todo={todo}/>
- );
- });
-
- return (
- <div className="todoList">
- <SearchBox/>
- {todoItems}
- </div>
- );
- }
现在可以预览一下页面效果:
下一步就要为SearchBox添加事件处理了,我们需要好好思考一下如何将文本框中的输入与TodoItems关联起来。首先,要想对数据集进行过滤,TodoList模块中必须定义一个表示搜索关键字的变量,进而对数据集进行操作,然后,这里的关键字是SearchBox提供的,所以SearchBox模块中的文本框数据变化必须通知TodoList模块,做下一步的处理。
现在我们来修改一下TodoList的代码:
- var TodoList = React.createClass({
- handleSearchTextUpdate: function(searchText) {
- this.state.searchText = searchText;
- this.setState(this.state);
- },getInitialState: function() {
- return {
- data: [],searchText: '' //adding a searchText,it will be used in render method
- };
- },time: "11:00"}
- ];
-
- this.state.data = mockData;
-
- this.setState(this.state);
- },render: function() {
- var state = this.state;
-
- //filter the data first and then call map function
- var todoItems = state.data.filter(function(todo) {
- return todo.name.toLowerCase().indexOf(state.searchText.toLowerCase()) > -1;
- }).map(function(todo) {
- return (
- <TodoItem key={todo.id} todo={todo}/>
- );
- });
-
- return (
- //adding a 'onSearchTextUpdate' callback,it will be called when text changes in search input
- <div className="todoList">
- <SearchBox onSearchTextUpdate={this.handleSearchTextUpdate}/>
- {todoItems}
- </div>
- );
- }
- });
可以看到,在getInitialState
方法中,我们添加了一个searchText
的值,用于表示过滤的关键字,然后在render方法中,我们会使用filter函数对数据集进行过滤,searchText也就是在这个时候发挥它的作用的。接着在SearchBox声明时我们添加了onSearchTextUpdate
属性作为搜索文本变化后的回调函数,它实际上会执行handleSearchTextUpdate函数,进而设置新的searchText,然后重置state刷新视图。
那么在SearchBox模块,我们需要做的就是监听文本框中输入的变化,然后调用上面提到的回调函数,把最新的输入传递上去,来看一下修改过的代码:
- var SearchBox = React.createClass({
- handleChange: function(event) {
- var newInput = event.target.value;
-
- //call the onSearchTextUpdate in props
- this.props.onSearchTextUpdate(newInput);
- },render: function() {
- return (
- //adding a 'onChange' to monitor the value changes
- <div className="searchBox">
- <input type="text" onChange={this.handleChange} placeholder="type in keywords to search"/>
- </div>
- );
- }
- });
过滤功能只是在TodoList和SearchBox之间的交互,TodoItem是不需要任何改动的,所以只需弄清楚SearchBox和TodoList之间的交互,就可以很快实现一个过滤功能,下面是我们最终的页面效果:
如果你是一个Angular的爱好者,可能觉得React使用起来会比较绕,那么你需要仔细揣摩整个实现过程,或许你还会稍微有点喜欢它呢~