上节课我们介绍了如何给每条todo
添加鼠标移入移出效果,我们在Item
组件加了state
并通过鼠标移入移出事件更新state
来驱动页面样式的改变。既然我们在视觉效果上已经实现了那么我们来实现功能层面吧。这节课我们来讲一下如何来完成我们的todo
。
回顾
在我们开始这节课的内容之前,我们来对上节课的内容来做一个简单的回顾,上节课的内容没有什么新的知识点,都是之前一再强调的问题。
- 事件绑定必须是个函数
- 通过
state
驱动页面样式的修改
以上便是上节课我们需要注意的点,接下来我们开始这节课的内容。
todo
状态
我现在像图中那样有了todo
,但是我们如何去处理todo
完成的逻辑呢?或者说我们有些事其实没有完成,但是我不小心勾选上了,怎么取消呢?可能有人说这个简单啊,我们们点一下前面的checkbox
不就行了嘛。
但是这样处理是不是只是我们页面上变了?我们可别忘了底部是有一个组件在统计的。而且我们还有个功能要一键清除掉我们已完成的todo
的,如果只是在页面上做了勾选或者取消勾选我们怎么能实现底部组件的功能呢?
那么我们这样的话,我们是不是要从组件state
方面来考虑啊?不然我们之前App
组件的state
中每条todo
对象都加一个done
属性干吗呢?
功能分析
那么既然我们知道要从state
层面来实现,那么首先我们要先来思考两个问题。
todo
是否完成的这个标识存放在哪个组件中?todo
勾选的操作发生在哪个组件中?
那么好,我们之前是不是都说了我们把所有的todo
信息都存放在了App
组件的state
里?App
组件的state
中存放了多条todo
对象,每个todo
对象都有一个done
属性来标识这条todo
是否完成。所以说todo
是否完成的标识存放在App
组件中。
其次我如果要完成某个todo
,从宏观角度上来说是不是要点击列表中我想要完成的todo
对应的checkbox
?那么这个checkbox
在哪?是不是在Item
组件中?所以,我们完成todo
的勾选操作是不是就在Item
组件中发生?
那么好,如果我们要完成一个todo
,是不是就需要点击这个todo
对应的checkbox
?是不是相当于利用Item
组件中的checkbox
然后来触发App
组件中state
的更新?
既然这样就好办了,我们这个功能分为两步
- 勾选
todo
对应的checkbox
触发事件 - 通过事件修改
App
组件的state
这么来看的话是不是就清晰了许多?既然我们勾选就触发事件,那么我们是不是要给这个checkbox
绑定事件?那么绑定什么事件呢?有听过有勾选事件吗?没听过,那么我们来想想当我们勾选了这个checkbox
那是不是一种改变?所以我们使用onChange
事件就可以了,我们绑定了事件不行啊,我们事件的回调方法得自己定义啊,通过回调来修改App
组件的state
这个操作我们是不是在添加todo
那一节就已经介绍过了。既然如此我们便来实现吧
功能实现
上文已经说明了实现这个功能的思路了,那么接下来我们先看一下代码吧:
render() { const { todo } = this.props; const { mouse } = this.state; return ( ... <label> <input onChange={this.handleCheck(todo.id)} type="checkbox" defaultChecked={todo.done} /> ... ) } handleCheck = id => { return e => { console.log(e.target.value); } } handleMouse = flag => {...}; }
多余的代码我就先省略了,我们先来看这个checkbox
对应的input
标签,我们既然要绑定事件,是不是要加上onChange
事件啊?设置回调为handelCheck
。我们的目的是什么?是不是勾选了之后马上将App
组件中state
里的该条todo
的对象的done
属性改掉?那么我们通过什么来识别是这条todo
呢?是不是通过id
属性?那么我们就将id
传给handleCheck
方法。
既然绑定好了事件,那么我们要定义handleCheck
方法,这个方法接收id
参数,但是我们看<input>
中直接调用了这个方法对不对?那么事件绑定必须要是函数,所以我们handleCheck
方法是不是要返回一个函数?这个函数中要去修改App
组件的state
,我们是不是要根据checkbox
的勾选状态来判断?所以说是不是要获取<input>
的值?这个事件是不是也发生在<input>
上?那么我们返回的这个函数是不是就可以通过e
参数来接收event
再通过e.target.value
来获取到勾选状态?好,我们来看一下结果是个什么样子:
我们发现当我们对todo
进行勾选或者是取消勾选,控制台中打印出来的都是on
,这是什么意思呢?我们来看代码。其实我们现在的checkbox
是不是都用input
标签然后来修改type
属性来实现的?我们之前通过event.target.value
能获取到值是因为之前input
标签的type
属性都是text
,当我们改成checkbox
之后就不能通过event.target.value
来取值了,得改成event.target.checked
。这是原生js
中的一些基础知识,我们这里就不在赘述了。那么我们修改一下代码
render() { const { todo } = this.props; const { mouse } = this.state; return (...} handleCheck = id => { return e => { console.log(id, e.target.checked); } } handleMouse = flag => {...}; }
我们现在已经将value
改成了checked
了,为了方便观察,我们吧id
也打印一下。那么再来看一下效果:
这一次我们便能拿到我们的todo
的id
以及todo
是完成了还是取消完成。那么我们是不是就要通知App
组件来更新state
了?我们之前也说了,子组件要更新父组件的state
是不是要父组件来传一个操作入口的函数给子组件?但是Item
是不是App
的子组件?当然不是啊。Item
是List
的子组件,而List
才是App
的子组件。那么我们是不是可以先将这个函数传给List
然后通过List
再传给Item
组件啊?那么好我吗来看一下行不行:
// App.js App 组件 export default class App extends Component { state = { todos: [] }; add = dataObj => {...}; update = (id, done) => { const { todos } = this.state; const newTodos = todos.map(todo => id === todo.id ? { ...todo, done } : todo); this.setState({ todos: newTodos }); } render() { const { todos } = this.state; return ( ... <List todos={todos} update={this.update} /> ... } }
我把一些暂时不需要关注的代码就先省略了,我们先来看App
组件我们添加了一个update
方法用来作为给子组件修改父组件中state
的入口函数。
那么来看这个方法,首先我们是不是要接收参数?我得先知道我们要更新哪条todo
啊,而且我们得知道更新成什么样子啊。所以我们要接收id
和done
。
然后我们是不是要先获取到当前我们所有的todos
?但是现在这个todos
是不是一个数组?我们需要将这个数组加工一下,是不是要遍历判断id
是否相等?如果相等则修改done
属性,否则不做改变。那么我们用map
方法就可以了啊。
我们直接用了三元运算符,但是这里{...todo, done}
是什么意思?这里可不是展开运算符啊。我们之前有一件专门讲过展开运算符的,如果大家忘了赶紧回去复习一下。我们{...obj}
这一步是不是在复制一个对象啊?而且{...obj, xxx: "xxxx"}
是不是可以在赋值obj
对象的时候修改掉其中的xxx
属性,那么我们这里也是,因为属性名和变量名一样,所以可以简写。
然后到了最后一步就是修改state
,这个方法就算完成了。
紧接着我们把这个方法传给List
组件,再由List
组件传给Item
组件,我们来看一下代码:
// List.jsx List 组件 export default class List extends Component { render() { const { todos, update } = this.props; return ( <ul className="todo-list"> {todos.map(todo => <Item key={todo.id} todo={todo} update={update} />)} </ul> ) } } // Item.jsx Item 组件 export default class Item extends Component { state = { mouse: false }; render() {...} handleCheck = id => { const {update} = this.props; return e => { update(id, e.target.checked); } } handleMouse = flag => {...}; }
我们在List
组件中解构赋值获取到props
中的update
函数,然后传给Item
组件,然后我们在Item
组件中的handleCheck
方法中来调用并将todo
的id
和checkbox
的勾选状态传给update
函数来完成对App
组件的state
的更新,那么我们来看一下效果吧:
当我们修改了各个todo
的勾选状态之后,App
组件的state
中每条todo
对象的done
属性也作出了相应的改变。可见我们这个方法成功实现了todo
是否完成的功能。
总结
checkbox
不能通过event.target.value
拿勾选状态,应该用event.target.checked
state
在哪,修改state
的方法就在哪- 事件绑定一定得是一个函数
Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.
Link of this article:https://work.lynchow.com/article/finish_todo/