您的位置:首页 > Web前端 > React

React入门实践之TodoList: 添加事件和搜索框

2016-05-23 12:53 627 查看
在上篇文章中,我们使用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
};
},
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.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使用起来会比较绕,那么你需要仔细揣摩整个实现过程,或许你还会稍微有点喜欢它呢~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: