一、利用组件化原理编写TodoList功能
- 利用map()方法对数组进行映射时,需要对重复显示的内容添加key属性,取值为具有唯一性的索引值。
- 对文本框中的数据进行双向绑定:
//JSX:
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
inputChange(event){
console.log(event.target.value); //得到用户在文本框中输入的内容
this.setState({
inputValue:event.target.value
})
}
- 在JSX中为HTML元素设置类名,应该使用className属性,而不是class属性。
- 如何在JSX代码中指定的事件里传递参数:
<span className="delete" onClick={this.deleteItem.bind(this,index)}></span>
//该案例引入了:<link rel="stylesheet" href="style/bootstrap.css">
//如果使用了Bootstrap,则不要使用.close类名
- 利用Bootstrap进行页面样式的改写
.container
.row
.col-md-12
文本框与按钮:
.input-group
input.form-control //文本框
.input-group-btn
button.btn.btn-primary //按钮
- 如何为HTML元素设置内联样式:
<li style={{marginBottom:’10px’}}><span>×</span>{item}</li>
二、将组件进行拆分
- 父组件向子组件传递数据:通过HTML属性来进行传递的。
子组件接收父组件传递的参数:this.props.属性名
。
父组件向子组件传递的数据属于单向传递,在子组件中不能修改父组件传递过来的数据。 - 子组件向父组件传递数据:通过父组件中的自定义事件来进行传递。
子组件中如何调用父组件的自定义事件:this.props.自定义事件名
。
效果图:
完整代码:
<div id="app"></div>
//组件TodoItem(子组件)
class TodoItem extends React.Component{
//删除待办事宜
deleteItem(index){
this.props.onCustom(index);
}
render(){
return(
<div>
<li key={this.props.index}>
<span className="delete" onClick={this.deleteItem.bind(this,this.props.index)}>×</span>
{this.props.title}
</li>
</div>
)
}
}
//组件TodoList(父组件)
class TodoList extends React.Component{
constructor(props){
super(props);
this.state = {
inputValue:'',
mylist:['早上7:30吃饭','早上8:30上班','中午12:00休息']
}
}
//添加待办事宜,文本框change事件
inputChange(event){
this.setState({
inputValue:event.target.value
})
}
//按钮点击事件
addClick(){
if(this.state.inputValue == ''){
alert('请输入待办事宜')
}else{
let mylist = [...this.state.mylist,this.state.inputValue];
this.setState({
mylist,
inputValue:''
})
}
}
//删除待办事宜
closeItem(index){
let mylist = [...this.state.mylist];
mylist.splice(index,1);
this.setState({
mylist
})
}
render(){
return (
<div>
<div className="container">
<div className="row"><h2>ToDoList (React版) <span style={{fontSize:'16px'}}>练习</span></h2></div>
<div className="row">
<div className="input-group">
<input className="form-control" type="text" value={this.state.inputValue} placeholder='请添加待办事宜'
onChange={this.inputChange.bind(this)}/>
<div className="input-group-btn">
<button className="btn btn-success" onClick={this.addClick.bind(this)}>添加待办事宜</button>
</div>
</div>
</div>
<div className="row" style={{marginTop:'20px'}}>
<ul className="mylist">
{
this.state.mylist.map((item,index)=>{
return <TodoItem title={item} index={index} key={index} onCustom={this.closeItem.bind(this,index)}></TodoItem>
})
}
</ul>
</div>
</div>
</div>
)
}
}
ReactDOM.render(
<TodoList></TodoList>,
document.getElementById('app')
)
三、代码优化
1. 事件中的this指向
我们在开发时希望this永远指向this所在的组件(类中)中
- 第一种解决方案
在组件的构造函数中,对所有的事件方法进行重写,这样在使用这些事件方法时就不需要再重新指定this指向了。
constructor(props){
super(props);
this.state={}; //组件的状态,相当于Vue中的数据区
//对组件中使用的事件进行重写
this.inputChange=this.inputChange.bind(this);
this.addTodo=this.addTodo.bind(this);
this.closeItem=this.closeItem.bind(this);
}
render(){
return(
<input type=“text” onChange={this.inputChange} />
)
}
- 第二种解决方案
使用箭头函数来书写事件代码
<input onChange={()=>this.inputChange(event)}>
<button onClick={()=>this.addTodo()}></button>
<TodoItem onCustom={()=>this.closeItem(index)} />
- 如果要在浏览器的控制台中看到组件实例,可以定义一个全局变量,在组件的构造函数中将this指向这个全局变量
let that;
class TodoList extends React.Component{
constructor(props){
that=this;
}
}
2. 如何在JSX代码中设置必须存在的根节点
问题:如果用普通的div或HTML标记对做根节点,这个跟节点将被渲染到页面中。
解决:使用<React.Fragment></React.Fragment>做根节点。
3. 使用对象解构的方式对React对象的属性进行操作
const {Component,Fragment} = React;
class TodoList extends Component{
render(){
return (
<Fragment></Fragment>
)
}
}
4. 子组件接收父组件传递的参数也可以使用对象解构的方式
const {text,index,onCustom} = this.props;
//具体的子组件方法中使用到哪个父组件属性就解构哪一个。
四、代码优化后结果
//组件TodoItem(子组件)
const {Component,Fragment} = React; //使用对象解构的方式对React对象的属性进行操作
class TodoItem extends Component{
//删除待办事宜
deleteItem(index){
//子组件接收父组件传递的参数也可以使用对象解构的方式
//具体的子组件方法中使用到哪个父组件属性就解构哪一个。
const {onCustom} = this.props;
onCustom(index);
}
render(){
const {title,index} = this.props;
return(
<Fragment>
<li key={index}>
<span className="delete" onClick={()=>this.deleteItem(index)}>×</span>
{title}
</li>
</Fragment>
)
}
}
//组件TodoList(父组件)
class TodoList extends Component{
constructor(props){
super(props);
this.state = {
inputValue:'',
mylist:['早上7:30吃饭','早上8:30上班','中午12:00休息']
}
}
//添加待办事宜,文本框change事件
inputChange(event){
this.setState({
inputValue:event.target.value
})
}
//按钮点击事件
addClick(){
if(this.state.inputValue == ''){
alert('请输入待办事宜')
}else{
let mylist = [...this.state.mylist,this.state.inputValue];
this.setState({
mylist,
inputValue:''
})
}
}
//删除待办事宜
closeItem(index){
let mylist = [...this.state.mylist];
mylist.splice(index,1);
this.setState({
mylist
})
}
render(){
return (
<Fragment>
<div className="container">
<div className="row"><h2>ToDoList (React版) <span style={{fontSize:'16px'}}>练习</span></h2></div>
<div className="row">
<div className="input-group">
<input className="form-control" type="text" value={this.state.inputValue} placeholder='请添加待办事宜'
onChange={()=>this.inputChange(event)}/>
<div className="input-group-btn">
<button className="btn btn-success" onClick={()=>this.addClick()}>添加待办事宜</button>
</div>
</div>
</div>
<div className="row" style={{marginTop:'20px'}}>
<ul className="mylist">
{
this.state.mylist.map((item,index)=>{
return <TodoItem title={item} index={index} key={index} onCustom={()=>this.closeItem(index)}></TodoItem>
})
}
</ul>
</div>
</div>
</Fragment>
)
}
}
ReactDOM.render(
<TodoList></TodoList>,
document.getElementById('app')
)