TODOList
这里没有对组件进行封装,而是全部写到了一个页面中 如果要对组件进行拆分的话,需要拆分为三个组件,分别是:
- 头部搜索框
 - 列表
 - 底部全选框
 
下面是比较标准的React开发规范
todolist├─ package.json├─ public│ ├─ favicon.ico│ └─ index.html├─ src│ ├─ App.css│ ├─ App.jsx│ ├─ Components│ │ ├─ Footer│ │ │ ├─ index.css│ │ │ └─ index.jsx│ │ ├─ Header│ │ │ ├─ index.css│ │ │ └─ index.jsx│ │ ├─ item│ │ │ ├─ index.css│ │ │ └─ index.jsx│ │ └─ List│ │ ├─ index.css│ │ └─ index.jsx│ └─ index.js└─ yarn.lock
需求分析
- 输入框中输入数据后,按下回车,可以将待办事项添加到列表中
 - 点击未完成的待办事项,可以将其标注为已完成。如果全部已完成,那么修改全选部分的状态
 - 鼠标悬浮到每一项后面的时候,出现删除按钮,点击删除按钮可以将元素删除
 - 全选部分可以控制待办列表的状态:全部已完成或全部未完成
代码实现
这里为了方便,直接引入了
ant-design组件库 
添加待办

给Input框绑定value以及onChange事件
<div><Inputvalue={this.state.text}onChange={(e) => this.setState({ text: e.target.value })}placeholder='请输入...'onKeyDown={this.handleEnterKeyDown}/></div>
按下回车要能触发事件,因此我们需要给输入框绑定onKeyDown事件,并判断按下的键是否是enter
不难发现,每一次新添加的内容需要添加到列表的首位,但其实利用ES6的解构运算,很容易实现,如下代码所示:
handleEnterKeyDown = (e) => {// 监听回车事件if (e.keyCode === 13) {const { text, list } = this.state;if (text.trim() === '') {message.error('请输入内容');} else {const { list } = this.state;let item = {id: list.length + 1,isFinished: false,text,};const newList = [item, ...list];let [sum, flag] = [0, false];newList.forEach((e) => (e.isFinished ? (sum += 1) : (sum += 0)));flag = sum === newList.length;this.setState({list: newList,text: '',isFinishedAll: flag,});message.success('添加成功');}}};
这里的小细节是,当输入之前,所有的待办处于已完成的状态,那么我们新添加一个待办之后,势必要将全选框的状态进行修改(因为此时已经不是全部都完成了)
完成待办
我们这里使用到了ant-design的列表组件,所以我们可以直接给List.Item绑定点击事件,从而进行逻辑的处理
有一个小细节,就是当所有的待办都被完成之后,要修改下面全选框的状态
删除待办
我们的需求是:鼠标悬浮在某一项上时,该项后面出现删除按钮。并且同一时间下有且仅有一项后面可以出现这个按钮。
因此我们需要监听鼠标的移入和移出事件,并且设定状态来判断鼠标悬浮于第几个待办上
全选/全不选
完整代码
import { Button, Checkbox, Divider, Input, List, message, Typography } from 'antd';import React, { Component } from 'react';export default class TodoList extends Component {state = {list: [{id: 1,isFinished: false,text: '吃饭',},],isFinishedAll: false,mouse: {in: false,index: -1,},text: '',};handleEnterKeyDown = (e) => {// 监听回车事件if (e.keyCode === 13) {const { text, list } = this.state;if (text.trim() === '') {message.error('请输入内容');} else {const { list } = this.state;let item = {id: list.length + 1,isFinished: false,text,};const newList = [item, ...list];let [sum, flag] = [0, false];newList.forEach((e) => (e.isFinished ? (sum += 1) : (sum += 0)));flag = sum === newList.length;this.setState({list: newList,text: '',isFinishedAll: flag,});message.success('添加成功');}}};handleFinish = (item) => {console.log(item);let { list } = this.state;if (!item.isFinished) {// 找出list中与item的id相同的元素let index = list.findIndex((e) => e.id === item.id);console.log('index', index);list[index].isFinished = true;this.setState({list,});message.success('恭喜您,任务完成!');}let sum = 0;list.forEach((e) => (e.isFinished ? sum++ : null));if (sum === list.length) {this.setState({isFinishedAll: true,});}};handleMouseOver = (flag, item) => {this.setState({mouse: {in: flag,index: item.id,},});};handleCheckBoxChecked = (e) => {const { checked } = e.target;let { list } = this.state;list.forEach((e) => (e.isFinished = checked));this.setState({list,isFinishedAll: checked,});const msg = checked ? '完成' : '取消';message.success(`已${msg}全部任务`);};handleDelete = (e, item) => {console.log('delete');e.stopPropagation();let { list } = this.state;this.setState({list: list.filter((e) => e.id !== item.id),});message.success(`已删除${item.text}`);};render() {return (<div><h2>待办事项</h2><div><Inputvalue={this.state.text}onChange={(e) => this.setState({ text: e.target.value })}placeholder='请输入...'onKeyDown={this.handleEnterKeyDown}/></div><><Divider orientation='left'>列表</Divider><Listfooter={<div><span><CheckboxonChange={this.handleCheckBoxChecked}checked={this.state.isFinishedAll}/></span>  <span>已完成{this.state.list.filter((e) => e.isFinished).length}项</span> / <span>全部{this.state.list.length}项</span></div>}bordereddataSource={this.state.list}renderItem={(item) => (<List.Itemactions={[<ButtononClick={(e) => this.handleDelete(e, item)}key='list-loadmore-edit'type='danger'style={{display:this.state.mouse.in === true && this.state.mouse.index === item.id? 'inline-block': 'none',}}>删除</Button>,]}onMouseOver={() => this.handleMouseOver(true, item)}onMouseOut={() => this.handleMouseOver(false, item)}onClick={() => this.handleFinish(item)}><Typography.Text mark={item.isFinished ? true : false}>[  ]</Typography.Text>{item.text}</List.Item>)}/></></div>);}}
