todolist-react版(9.20-9.21)
# 设计效果
![](https://img2020.cnblogs.com/blog/809476/202110/809476-20211006115709893-1422883391.png)
一. 框架 设计一个todolist,可以添加并记录今天做的事情,分为未完成和已完成,并且每个记录可以删除,并且统计记录总数和完成的数量 # 数据结构 父组件todoList包括输入框添加组件和列表数据组件 # 组件划分 todoList组件/输入框添加组件/列表数据组件 # 具体实现 父组件list 把添加任务部分和列表部分记录数据集合起来,用来实现添加任务,更新任务总数,完成任务总数 1. 父组件标题是ToDoList, h1, 未完成的样式,背景颜色#DFFCB5,color 颜色 #2EB872,完成样式,背景颜色#FFFA9D,颜色#FF9A3C,删除线 textDecoration: 'line-through' 2. 初始化时设置列表list的默认数据及初始化完成数量参数 3. 父组件添加任务方法更新任务列表list;完成任务状态改变时更新完成数量并更新;更新总数方法当删除时更新总数及对象传出去. 4. render(){return ()},dom包括标题/列表数据需要通过map来遍历/完成数和总数 列表item组件 勾选完成和删除数据,显示列表数据 1. 处理完成的任务,完成时需要切换状态,并把id,名称,状态等数据通过props传出去 2. 删除把props接收到的item传出去,组件那里需要通过item={item}来接收 3. render(){return ()},dom来处理完成和未完成样式及勾选框/名称/删除按钮 4. 初始化时绑定事件,如this.handleFinished = this.handleFinished.bind(this) 输入任务,添加任务组件 给input输入框绑定ref事件,添加任务判断任务列表个数来设置id,添加任务并清空输入框 1. 添加任务,通过绑定ref来获取到输入框的值并判断值是否为空,如为空提示信息并返回,否则添加任务,添加任务时通过props获取到列表总数来设置id,如果列表总数大于0,id取列表总数值否则为0,如果输入框存在且值不为空时,设置对象id/name(输入框的值)/状态,清空输入框的值,通过props添加任务 2. render(){return ()},dom Task子标题,输入框绑定ref,按钮处理添加任务 # 样式分析 父组件 container # 复盘应用到的知识 1. react 实现构造方法constructor,必须实现super(); 2. react 绑定ref 需要建立ref,例子 this.myText = React.createRef(),dom ref={this.myText}. 3. react的事件需要绑定的,例子 处理单击事件this.handleClick = this.handleClick.bind(this). 4. 获取input的值,需要获取到current对象中的value. 5. 调用父组件方法,例子this.props.addNewTask(obj),调用父组件的值 this.props.item.status. 6. 调用state中的值,this.state, 更新state,this.setState({name: 'sdf'}). 7. react render (){} 函数中可以添加变量以方便在return()中使用 8. react 设置样式有两种,一是通过className="name",另一个种是style={style},style是一个对象,每个样式结束用逗号隔开. 9. 在react 中遍历数组时用map,且组件也就是=>后面的组件不需要用{}来包裹 10. 媒体尺寸适配, @media screen and (max-width: 767px) { .container { ... } h3 { .... } }
上代码如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>ToDoList</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="./main.css"> <script src="../Build/react.development.js"></script> <script src="../Build/react-dom.development.js"></script> <script src="../Build/babel.min.js"></script> </head> <body> <div id="app"></div> <script type="text/babel"> // todolist 输入框 class Dialog extends React.Component { constructor (props) { super(props) this.myText = React.createRef(); this.handleClick = this.handleClick.bind(this); } handleClick () { if (!this.myText.current.value) { alert('请添加你的任务') return } const len = this.props.nums; const newId = len > 0 ? len : 0; const value = this.myText.current.value; if (!value !== '') { const obj = { id: newId, name: value, status: 0 }; this.myText.current.value = ''; this.props.addNewTask(obj); } } render () { return ( <div className="dialog"> <div> <h3>Task</h3> <input type="text" placeholder="今天做什么" ref={this.myText}/> <input type="button" value="添加" onClick={this.handleClick}/> </div> </div> ) } } // listItem class ListItem extends React.Component { constructor(props) { super(props) this.handleFinished = this.handleFinished.bind(this) this.handleDelete = this.handleDelete.bind(this) } // 处理todolist完成 handleFinished () { let status = this.props.item.status status = (status === 0 ? 1 : 0) let obj = { id: this.props.item.id, name: this.props.item.name, status: status } this.props.finishChange(obj) } // 删除todolist一项 handleDelete () { this.props.totalChange(this.props.item) } render () { const item = this.props.item const unfinish = { backgroundColor: '#DFFCB5', color: '#2EB872' } const finish = { backgroundColor: '#FFFA9D', color: '#FF9A3C', textDecoration: 'line-through' } var itemStyle = item.status === 0 ? unfinish : finish; return ( <li key={item.id} style={itemStyle}> <span onClick={this.handleFinished} id={item.id} className="check-btn" style={{backgroundColor: item.status === 0 ? '#fff': '#A1EAFB'}}> </span> <span>{item.name}</span> <span onClick={this.handleDelete} className="delete-btn">删除</span> </li> ) } } // 数组 class ToDoList extends React.Component { constructor(props) { super(props) this.state = { list: [{ id: 0, name: 'react学习', status: 0 }, { id: 1, name: 'vue学习', status: 0 }], finished: 0 } } addTask (newItem) { var allTask = this.state.list; allTask.push(newItem) this.setState({ list: allTask }); } updateFinished (todoItem) { console.log('------------updateFinished', todoItem); var sum = 0; this.state.list.forEach((item) => { if (item.id === todoItem.id) { item.status = todoItem.status; } if (item.status === 1) { sum++; } }); this.setState({ finished: sum }) } updateTotal (todoItem) { var obj = [] var sum = 0 this.state.list.forEach((item) => { if (item.id !== todoItem.id) { obj.push(item) if (item.status === 1) { sum++ } } }) this.setState({ list: obj, finished: sum }) } render () { return ( <div className="container"> <h1>toDoList</h1> <Dialog addNewTask={this.addTask.bind(this)} nums={this.state.list.length}/> <ul> { this.state.list.map((item, index) => <ListItem item={item} finishChange={this.updateFinished.bind(this)} totalChange={this.updateTotal.bind(this)} key={index}> </ListItem> ) } <li>{this.state.finished}已完成 / {this.state.list.length}总数</li> </ul> </div> ) } } ReactDOM.render( <ToDoList/>, document.getElementById('app') ) </script> </body> </html> // main.css .container { text-align: center; background-color: #f7f7f7; margin: 50px 20%; padding: 20px 0; height: 100%; border-radius: 5px; } .dialog { text-align: left; margin: 0 5%; padding: 30px 0; } .dialog div { /* overflow: hidden; */ display: flex; justify-content: center; align-items: center; } h3 { display: inline-block; margin-left: 50px; margin-right: 20px; /* position: relative; top: 50%; transform: translateY(-50%); */ } input[type="text"] { line-height: 40px; /* float: right; */ width: 80%; border-radius: 5px; border: none; font-size: 16px; box-sizing: border-box; padding: 0 5px; border: 1px #ccc solid; outline: none; box-shadow: 0 0 10px #ccc; } input[type="button"] { margin-left: 20px; border: none; background-color: #A1EAFB; height: 40px; width: 110px; font-size: 18px; color: #fff; border-radius: 5px; /* float: right; */ } ul { text-align: left; margin: 20px 5%; border-radius: 5px; border: 1px #bbb solid; } ul li { list-style: none; line-height: 50px; border-bottom: 1px #bbb solid; padding: 0 20px; } ul li:first-child { /* border-top-left-radius: 5px; border-top-right-radius: 5px; */ } ul li:last-child { /* border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; */ } ul li:hover > .delete-btn { display: block; } .delete-btn { color: #ccc; float: right; cursor: pointer; display: none; } .check-btn { width: 13px; height: 13px; display: inline-block; border: 1px #ccc solid; border-style: outset; border-radius: 5px; margin-right: 20px; cursor: pointer; } /* input[type="checkbox"] { border: 0; color: #fff; background-color: #A1EAFB; } */ @media screen and (max-width: 767px) { .container { margin: 20px 5%; } h3 { margin-left: 0; }
将来的自己,会感谢现在不放弃的自己!