在上篇文章中,我们使用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模块:
var SearchBox = React.createClass({
render: function() {
return (
<div className="searchBox">
<input type="text" placeholder="type in keywords to search"/>
</div>
);
}
});
然后声明对应的CSS样式:
.searchBox { width: 400px; height: 100%; margin: 0 auto; }
.searchBox input { width: 100%; height: 30px; border: none; padding: 5px 15px; border-radius: 2px; font-size: 14px; }
最后还需在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使用起来会比较绕,那么你需要仔细揣摩整个实现过程,或许你还会稍微有点喜欢它呢~