对慕课网上的付费教程:React.js入门与实战中的todolist一章的源码分析.
Part 1 :达到目标
一个输入框,在其中输入内容后回车,其中的内容会显示在输入框下面:
点击对应的内容会消失.
Part 2:分析
首先,这里有两部分组成,第一,输入框,第二,展示区域,所以分别分为两个组件:Input.jsx
和List.jsx
.按照教程上讲,这两个就是木偶组件,即只负责数据的传递与展示.在这之上还有另一个整合这两个组件的智能组件,负责数据的处理Todo.jsx
.
Todo.jsx
这里首先思考一个问题,我们操作的数据,即用户输入完成后保存在List.jsx
中的数据保存在哪里?保存在List.jsx
中吗?不能,因为这部分数据与Input.jsx
中有关,当Input.jsx
中需要传入新的值时,需要重新进行输出.那保存在Input.jsx
中可以吗?可以,那么List.jsx
还有什么存在的意义吗?所以,当涉及到两个组件的公共数据时,都是保存在调用这两个组件的父组件上的.即Todo.jsx
中的.
import React from 'react'
impoer Input from 'Input'
import List from 'List'
class Todo extends React.Component{
constructor(props,context){
super(props,context);
this.state={
// 保存Input.jsx和List.jsx的公共值
todos:[]
}
}
render(){
return(
<div>
<Input submitFn={this.submitFn.bind(this)}/>
<List todos={this.state.todos} deleteFn={this.deleteFn.binf(this)}
</div>
)
}
}
暂时就看这么多,可以看到在Todo.jsx
中调用Input.jsx
和List.jsx
组件时,还向其中传递了两个函数对象参数:submitFn
和deleteFn
,并将数据this.state.todos
作为参数传递到下层组件去.这里思考一下,传递的这几个参数作用是什么?
答案揭晓:
- submitFn:将用户最新的输入传递到
this.state.todos
中进行输出 - deleteFn:给
List.jsx
调用,来操作Todo.jsx
中的this.state.todos
- todos:将数据传递给
List.jsx
进行输出.
submitFn
submitFn(value){
// 获取 this.state.todos数组的下一个下标,当作id
const id=this.state.todos.length;
// 调用this.setState来修改this.state的值
this.setState({
// concat 的作用是连接两个数组,即旧的this.state.todos和{id:id,text:value}
todos:this.state.todos.concat({
id:id,
text:value
})
})
}
注意,这里的submitFn
等于是Todo.jsx
中开放给Input.jsx
操作父级组件中的this.state.todos
的一个函数,一个函数,所以可以需要设置传入的参数value
.
deleteFn
类似的,deleteFn
就是Todo.jsx
开放给List.jsx
来删除父组件中的this.state.todos
的值的.
deleteFn(id){
let data=this.state.todos;
this.setState({
// ES6语法 ()=>{}
// 这里表名,如果当前项的id与传入的id不相同,则返回真,否则返回false
todos:data.filter(item=>{
if(item.id!==id){
return item
}
})
});
}
Input.jsx
首先,输入值肯定是保存在state
中的,并且需要为该输入框绑定onChange
事件,并且我们需要实现,在输入完成后可以按Enter
键将内容传递到List.jsx
中进行输出.所以基本结构就是这样:
import React from 'react'
class Input extends React.Component{
constructor(props,context){
super(props,context);
// 初始化 state,设置value来保存输入框输入
this.state={
value:""
}
}
render(){
return(
<div>
<input
{/*绑定value的值*/}
value={this.state.value}
{/*绑定表单内容change事件*/}
onChange={this.changeHandler.bind(this)}
{/*绑定键盘单击事件*/}
onKeyUp={this.keyUpHandler.bind(this)}
</div>
)
}
}
事件的命名规则一般是XXXHander
接下来重点就是绑定的这两个事件该怎么写了,changeHandler
是简单的,就是将最新的输入值保存进this.state.value
中:
changeHandler(event){
this.setState({
value:event.target.value
});
}
这里的event
参数的传入是依据ES5的标准,所以不需要在上面进行显式的指定.
接下来就是键盘单击事件了,这里需要实现的目标是当用户点击Enter键,并且输入框内数据不为空时,将数据传递到List.jsx中进行输出.具体实现就是需要父级Input.jsx
中传入的函数对象参数:submitFn了,具体调用方式如下:
keyUpHandler(event){
const value=this.state.value;
// 当用户单击Enter键并且输入值不为空
if(event.keyCode===13 && value.trim()){
// 注意这里的调用方式:this.props
this.props.submitFn(value);
// 之后可以清空一下输入框,提升用户体验
this.setState({value:""})
}
}
这样就实现了,当用户输入完成后,正确的输入值会被保存在Input.jsx
中的this.state.todos
中了.
List.jsx
这一组件的作用其实就两个,第一,输出内容,第二,绑定单击事件,用于实现单击时删除功能.
import React from 'react'
class List extends React.Component{
render(){
// 获取从父级传入进来的参数
var data=this.props.todos;
// 根据传入参数进行输出
return (
<div>
<ul>
{/*由于这里的输出跟父级的this.state.todos绑定了,所以在父级中修改this.state.todos就能自动实现自动重新输出了*/}
{data.map((item,index)=>{
return <li key={index} onClick={this.clickHandler.bind(this,item.id)}>{item.text}</li>
})}
</ul>
</div>
)
}
// 创建删除函数,删除的功能通过调用父级的函数实现
clickHandler(id){
this.props.deleteFn(id);
}
}
export default List
Part 3 总结
所谓智能组件和木偶组件,其中Todo.jsx
在其中的作用就是智能组件,负责数据处理,输出控制,而List.jsx
和Input.jsx
则是木偶组件,仅负责采集数据,输出数据,调用智能组件提供的接口,将数据传回智能组件进行处理.