0302TodoList案例-react应用

news/2024/11/29 0:35:22/

文章目录

    • 1 效果
    • 2 功能拆分和静态组件
    • 3 动态初始化
    • 4 功能实现
      • 4.1 添加todo
      • 4.2 鼠标移入效果和删除todo
      • 4.3 todo选中和取消选中
      • 4.4 底部统计和删除已完成
    • 5 TodoList案例总结
    • 结语

1 效果

通过前面学习React基础和create-react-app脚手架,下面我们做一个经典的入门案例TodoList。效果如下图1-1所示:

在这里插入图片描述

2 功能拆分和静态组件

首先对页面进行拆分,根据功能相关拆分如下图2-1所示组件:

在这里插入图片描述

拆分为4个组件如下:

  • Header组件:对应输入框部分
  • List:对应待办事项列表
    • Item:对应每一个待办事项
      • 包括复选框,文本,鼠标悬浮效果,删除操作等
  • Footer:底部,包括复选框,因完成和总数统计即清楚已完成功能

静态页面如下:

<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>React App</title><link rel="stylesheet" href="index.css">
</head>
<body>
<div id="root"><div class="todo-container"><div class="todo-wrap"><div class="todo-header"><input type="text" placeholder="请输入你的任务名称,按回车键确认"/></div><ul class="todo-main"><li><label><input type="checkbox"/><span>xxxxx</span></label><button class="btn btn-danger" style="display:none">删除</button></li><li><label><input type="checkbox"/><span>yyyy</span></label><button class="btn btn-danger" style="display:none">删除</button></li></ul><div class="todo-footer"><label><input type="checkbox"/></label><span><span>已完成0</span> / 全部2</span><button class="btn btn-danger">清除已完成任务</button></div></div></div>
</div></body>
</html>

静态样式如下:

/*base*/
body {background: #fff;
}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;
}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;
}.btn-danger:hover {color: #fff;background-color: #bd362f;
}.btn:focus {outline: none;
}.todo-container {width: 600px;margin: 0 auto;
}
.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;
}/*header*/
.todo-header input {width: 560px;height: 28px;font-size: 14px;border: 1px solid #ccc;border-radius: 4px;padding: 4px 7px;
}.todo-header input:focus {outline: none;border-color: rgba(82, 168, 236, 0.8);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}/*main*/
.todo-main {margin-left: 0px;border: 1px solid #ddd;border-radius: 2px;padding: 0px;
}.todo-empty {height: 40px;line-height: 40px;border: 1px solid #ddd;border-radius: 2px;padding-left: 5px;margin-top: 10px;
}
/*item*/
li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;border-bottom: 1px solid #ddd;
}li label {float: left;cursor: pointer;
}li label li input {vertical-align: middle;margin-right: 6px;position: relative;top: -1px;
}li button {float: right;display: none;margin-top: 3px;
}li:before {content: initial;
}li:last-child {border-bottom: none;
}/*footer*/
.todo-footer {height: 40px;line-height: 40px;padding-left: 6px;margin-top: 5px;
}.todo-footer label {display: inline-block;margin-right: 20px;cursor: pointer;
}.todo-footer label input {position: relative;top: -1px;vertical-align: middle;margin-right: 5px;
}.todo-footer button {float: right;margin-top: 5px;
}
  • 静态组件

create-react-app脚手架初始化项目,把不需要的去除,建立相应的组件,项目文件结构如下图3-1所示:

在这里插入图片描述

静态组件构建步骤:

  • 把div#root之下的所有结构放入App组件
  • 修改类名和style
    • class修改为className
    • style=“”,修改为jsx语法
  • 把样式放入App.css,查看页面效果。
  • 拆分对应的4个组件和样式,在App中引入对应3个子组件,List组件引入Item子组件。

App.jsx代码2-1如下图所示,其他组件不做展示:

// 创建外壳组件App
import { Component } from 'react'
import Header from './components/Header';
import List from './components/List';
import Footer from './components/Footer';
import './App.css'class App extends Component {render() {const { todos } = this.statereturn (<div className="todo-container"><div className="todo-wrap"><Header /><List /><Footer /></div></div>)}}export default App

3 动态初始化

首先我们来看下该应用场景下数据如何传递,如下图3-1所示:

在这里插入图片描述

我们的数据todos待办事项列表为同一份数据,而这三个组件给兄弟组件,想要进行数据交互,目前为止我们可以把该数据放置在它们共同的父组件App的状态中。

App.jsx组件state初始化代码3-1如下所示:

  /*** 待办事项列表*/state = {todos: [{ id: '001', name: '晨练', done: false },{ id: '002', name: '学习spring', done: true },{ id: '003', name: '逛街', done: true },{ id: '004', name: '晚饭', done: false },]}

第一步在List中展示待办事项,通过App组件props传递代码如下3-2所示:

  render() {const { todos } = this.statereturn (<div className="todo-container"><div className="todo-wrap"><Header /><List todos={todos}/><Footer /></div></div>)}

第二步 List中循环展示Item,List.jsx代码3-3如下所示:

import React, { Component } from 'react'
import Item from '../Item';
import './index.css'export default class List extends Component {render() {const {todos, updateTodo, deleteTodo} = this.propsreturn (<ul className="todo-main">{ todos.map(todo => {return <Item {...todo} key={todo.id}/>})}</ul>)}
}

第三部 Item 展示具体的信息,Item.jsx代码3-4如下:

import React, { Component } from 'react'
import './index.css'export default class Item extends Component {render() {const { name, done } = this.propsreturn (<li><label><input type="checkbox" defaultChecked={done}/><span>{name}</span></label><button className="btn btn-danger" style={{ display: 'none' }}>删除</button></li>)}
}

4 功能实现

4.1 添加todo

Header.jsx初始代码如下4.1-1所示:

import React, { Component } from 'react'
import './index.css'export default class Header extends Component {render() {return (<div className="todo-header"><input type="text" placeholder="请输入你的任务名称,按回车键确认" /></div>)}
}

添加Todo待办事项,流程:

  • 输入框输入待办事项名称
  • 敲击键盘回车键,完成Todo添加

我们在Header组件需要做的事情:

  • 输入框添加change事件监听;
  • change事件监听回调执行接收输入框数据,判断如果是回车键,把数据传入App组件;
  • App根据接收的数据,更新state,驱动页面更新。

具体实现:

  • 输入框添加change事件回调,代码4.1-2:

    <input type="text" placeholder="请输入你的任务名称,按回车键确认" onKeyUp={this.handleKeyUp} />
    
  • 回调函数代码如下4.1-3:

    /*** 键盘事件回调* @param {object} event * @returns */handleKeyUp = (event) => {const {keyCode, target} = event// 判断输入enter获取值if (keyCode !== 13) {return}// 校验数据,非空const val = target.value.trim() || target.valueif (!val) {alert('待做事项不能为空!')return}// 子组件给父组件传值,通过函数调用// 构建todo对象const todo = {id: nanoid(), name: val, done: false}this.props.addTodo(todo)// 清空输入框target.value = ''}
    
    • 父子传值,父组件给子组件传值通过props传递;子组件给父组件传值,子组件通过调用调用props接收父组件传递的函数的参数,传递给父组件。
    • 我们直接传递todo对象,待办事项id通过nanoid库生成。
  • App组件接收todo对象,更新state代码4.1-4:

    /*** 添加待办事项* @param {*} todo 待办事项*/
    addTodo = (todo) => {this.setState({ todos: [todo, ...this.state.todos] })
    }
    <Header addTodo={this.addTodo} />
    

4.2 鼠标移入效果和删除todo

鼠标移入直观效果,Item高亮同时显示删除按钮,可以删除当前Item项。

流程如下:

  • 鼠标移入,当前待办事项高亮,同时显示删除按钮;
  • 点击删除,给出提示;点击”确定“,删除当前待办事项,取消啥也不做。

Item组件需要操作:

  • li标签样式根据鼠标移入状态改变;
  • li标签添加鼠标移入事件和鼠标移出事件,同一个回调函数;
  • 通过回调函数接收的参数修改鼠标状态;
  • 删除按钮监听鼠标点击事件;
  • 回调函数获取todo的id向上传递父组件List,List组件向上传递App组件。App组件根据接收的todo的id删除相应的todos列表中的todo对象。

具体实现:

  • Item.jsx代码4.2-1:

    import React, { Component } from 'react'
    import './index.css'export default class Item extends Component {/*** 鼠标是否悬浮*/state = { mouse: false // 鼠标进入true;鼠标离开false}/*** 鼠标进入离开标志* @param {boolean} flag 鼠标进入true;鼠标离开false*/handleMouse = (flag) => {return () => {this.setState({ mouse: flag })}}/*** 处理复选框选中状态*  状态改变,获取id和状态值向上传递* @param {strig} id * @returns 回调函数*/handleCheck = (id) => {return (e) => {// 获取checkbox选中状态const done = e.target.checked// id,done向上传递const {updateTodo} = this.propsupdateTodo(id, done)}}/*** 删除一个todu回调* @param {string} id */handleDelete = (id) => {// 校验// 原生方法需要指定window前缀if (window.confirm("您确定要删除吗?")) {// 向上传递idthis.props.deleteTodo(id)}}render() {const { id, name, done } = this.propsconst { mouse } = this.statereturn (<li style={{ backgroundColor: mouse ? '#ddd' : '#fff' }} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}><label><input type="checkbox" checked={done} onChange={this.handleCheck(id)}/><span>{name}</span></label><button className="btn btn-danger" style={{ display: mouse ? 'block' : 'none' }} onClick={() => this.handleDelete(id)}>删除</button></li>)}
    }
  • List组件代码4.2-2:

    <ul className="todo-main">{ todos.map(todo => {return <Item {...todo} key={todo.id} deleteTodo={deleteTodo}/>})}
    </ul>
    
  • App组件代码4.2-3:

      /*** 根据id删除todo* @param {string} id */deleteTodo = (id) => {// 获取todo列表const { todos } = this.state// 设置todo对象的done 为trueconst newTodos = todos.filter((todo) => {return todo.id !== id})// 更新状态this.setState({ todos: newTodos })}<List todos={todos} deleteTodo={this.deleteTodo} />
    

4.3 todo选中和取消选中

效果:

  • 点击待办事项复选框选中;再次点击取消选中。

组件操作:

  • Item组件复选框监听change事件;
  • 回调函数向上传递todo对象id,done属性;
  • App组件更加传递的id修改相应todo对象的done属性。

具体实现:

  • Item组件代码4.3-1:

    /*** 处理复选框选中状态*  状态改变,获取id和状态值向上传递* @param {strig} id * @returns 回调函数*/handleCheck = (id) => {return (e) => {// 获取checkbox选中状态const done = e.target.checked// id,done向上传递const {updateTodo} = this.propsupdateTodo(id, done)}}<input type="checkbox" checked={done} onChange={this.handleCheck(id)}/>
    
    • 这里我们输入框属性由defaultChecked改回checked,想想为什么?
  • List组件代码4.3-2:

    <ul className="todo-main">{ todos.map(todo => {return <Item {...todo} key={todo.id} updateTodo={updateTodo} deleteTodo={deleteTodo}/>})}
    </ul>
    
  • App组件代码4.3-3:

      /*** 更新待办事项* @param {string} id 待办事项唯一标识* @param {boolean} done 待办事项是否已完成*/updateTodo = (id, done) => {console.log(id, '==', done);// 获取待办事项列表const { todos } = this.state// 根据id查找待办事项,修改是否已完成const newTodos = todos.map(todo => {// 根据id判断是否目标待办事项if (todo.id === id) {return { ...todo, done }} else {return todo}})// 更新状态this.setState({ todos: newTodos })}<List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo} />
    

4.4 底部统计和删除已完成

效果:

  • 底部显示已完成待办事项数量和总数量,更加上面选中或者取消实时改变;
  • 当上面全部选中后,下面复选框也选中;否则不选中;
  • 点击复选框,全选;再次点击全取消选中;
  • 点击删除已完成,删除已完成待办事项;

组件操作:

  • 已完成通过统计todos列表中done为true的项;总数量为数组长度;
  • 复选框属性checked初始值通过判断已完成数量和总数量算法相等;复选框监听change事件,回调函数向上传递复选框的值;
  • 复选框全选,遍历todos,修改done为true;复选框全不选,遍历todos,修改每个todo的done值为false;
  • 删除按钮监听鼠标点击事件,回调函数向上传递;

具体实现:

  • Footer组件代码4.4-1:

    import React, { Component } from 'react'
    import './index.css'export default class Footer extends Component {/*** 选中全部复选框回调*/handleAllCheck = (event) => {this.props.checkAllTodo(event.target.checked)}/*** 清除所有已完成回调*/handleAllDone = () => {this.props.deleteAllDone()}render() {const { todos } = this.props// 1 已完成总数const doneCount = todos.reduce((pre, current) => pre + (current.done ? 1 : 0), 0)// 2 总数const total = todos.lengthreturn (<div className="todo-footer"><label><input type="checkbox" onChange={this.handleAllCheck} checked={doneCount === total && total !== 0} /></label><span><span>已完成{doneCount}</span> / 全部{total}</span><button className="btn btn-danger" onClick={this.handleAllDone}>清除已完成任务</button></div>)}
    }
    
  • App组件代码4.4-2:

      /*** 选中全部复选框*/checkAllTodo = (done) => {// 获取todo列表const { todos } = this.state// const newTodos = todos.map((todo) => {return { ...todo, done }})// 更新状态this.setState({ todos: newTodos })}/*** 删除已完成*/deleteAllDone = () => {// 获取todo列表const {todos} = this.state// 设置todo对象的done 为trueconst newTodos = todos.filter((todo) => {return !todo.done})// 更新状态this.setState({todos: newTodos})}<Footer todos={todos} checkAllTodo={this.checkAllTodo} deleteAllDone={this.deleteAllDone}/>
    

5 TodoList案例总结

  • 拆分组件和实现静态组件
    • 注意className和style些饭
  • 动态初始化列表,如何确定数据放在那些组件的state中?
    • 某个组件使用,放在其自身的state中
    • 某些组件使用,放在其共同的父组件中
  • 父子组件通信
    • 父组件给子组件传值:通过props传递
    • 子组件给父组件传值:父组件通过props给子组件传递一个函数,子组件执行该函数通过参数传递。
  • 注意defaultChecked和checked区别
    • defaultChecked:初始状态,初始生效;
    • checked:非初始状态,如需修改,需要通过监听change事件修改。
  • 状态在哪里,操作状态的操作放置在哪里。

关于对传递数据必要性和类型限制这里不在详述,完整的代码参考底部代码仓库。

结语

❓QQ:806797785

⭐️源代码仓库地址:https://github.com/gaogzhen/react-staging.git

参考:

[1]React视频教程[CP/OL].2020-12-15.p56-64.

[2]React官网[CP/OL].

[2]ChatGPT[CP/OL].


http://www.ppmy.cn/news/41739.html

相关文章

记一次IDEA(linux版) cpu占用太高的处理过程

早上打开电脑->打开idea&#xff0c;cpu飙到97%&#xff0c;加载完索引&#xff0c;加载完所有项之后&#xff0c;吾发现idea cpu占用并没有释放. 重启idea问题依旧&#xff0c;重启电脑问题依旧&#xff0c;吾甚是难以理解。 于是打开如下(汉化后的idea)&#xff1a; 发现…

Spring开启事务流程和事务相关配置

文章目录Spring事务Spring快速入门事务相关配置Spring事务 Spring快速入门 事务作用&#xff1a;在数据层保障一系列的数据库操作同成功同失败 Spring事务作用&#xff1a;在数据层或业务层保障一系列的数据库操作同成功同失败 Spring提供了一个接口PlatformTransactionMana…

零基础学习Java 08

目录 继承 super关键字 继承中构造方法的关系 继承使用场景 继承的设计技巧 多态 多态中的成员访问特点 继承 继承&#xff1a;多个类中存在相同属性&#xff08;成员变量&#xff09;和行为&#xff08;成员方法&#xff09;时&#xff0c;将这些内容抽取到单独一个类中…

CS5463 DP转HDMI8K30Hz转换芯片规格书|CS5466 typec转HDMI8K30Hz (4K144Hz)转换芯片规格书

1.CS5463/CS5266概述 CS5463/CS5466是一款高性能的Type-C/DisplayPort1.4到HDMI2.1协议转换器&#xff0c;通过Type-C/D DisplayPort链路接收视频和流&#xff0c;并转换为支持TMDS或FRL输出信令的HDM。DP接收器在2个通道上支持高达8.1 Gbps的链路速率。HDMI输出端口可以作为T…

类的相关知识(一)

目录 OOP&#xff08;面向对象编程&#xff09; 区别 面向过程 面向对象 类 成员函数 类的作用域 类的大小 this指针 OOP&#xff08;面向对象编程&#xff09; 本质上是一种编程思想&#xff0c;通过把我们编程中遇到的事物抽象成对象来编程 区别 面向过程 struc…

SQL Day01

1.关系型数据库的特点 理论基础&#xff1a;关系代数&#xff08;集合论、一阶逻辑、关系运算&#xff09; 具体表象&#xff1a;用二维表装数据 ​ - 表 - table / entity - relation ​ - 列 - column / field - attribute ​ - 行 - row / record - tuple ​ - 列的数量 - …

不完整数据下视听情感识别的自注意融合

Self-attention fusion for audiovisual emotion recognition with incomplete data 译文&#xff1a;不完整数据下视听情感识别的自注意融合 摘要&#xff1a;在本文中&#xff0c;我们以视听情感识别为例&#xff0c;考虑了多模态数据分析的问题。我们提出了一种能够从原始…

Excel快捷键

ctrlQ 快速分析&#xff0c;对数据进行快速分析&#xff0c;如求和&#xff0c;图表、格式化数据等 ctrlT 转化为超级表&#xff0c;超级表的功能很多&#xff0c;很便捷 ctrlE 快速填充&#xff0c;能够根据规律自动填充对应的数据提出或拼接 ctrl&#xff1b;ctrlshift&#…