使用React 实现一个简单的待办事项列表:用户可以添加、编辑和删除待办事项 | 青训营

本文主要介绍一下通过react实现一个简单的代办事件列表。

本文主要是记录一下实现过程以及后续改进方向。项目具体源码见仓库:gitee.com/piao-chen/r...

项目搭建

使用react脚手架进行搭建。首先运行命令:npm install -g create-react-app进行安装react脚手架。安装成功后,使用create-react-app XXX进行创建项目。

项目创建后的目录结构如下:

由于本项目要求十分简单,就不再拆分组件了,直接书写在App.js中。全局样式书写在index.css中,App.css中书写App组件的样式。

具体代码分析

页面结构

jsx 复制代码
<div>
  <div>
    <header>
      <span>待办事项</span>
    </header>
    <section>
      {
        list.length === 0 
          ? <span className='no-data'>暂无数据</span> 
          : 
          <ul>
            {
              list.map(item => {
                return (
                  <li key={item.id}>
                    <div></div>
                    <span>{item.thing}</span>
                    <button>
                      编辑
                    </button>
                    <button>
                      点击完成
                    </button>
                  </li>
                )
              })
            }
          </ul>
      }
    </section>
    <footer>
      <button>{'+   添加事项'}</button>
    </footer>
  </div>
</div>

上面为待办列表的html结构,使用了语义化结构,也是对前面的语义化结构知识的一些复习。整体分为三个部分,分别为标题区域,列表主体区域以及底部添加待办区域。

样式添加

下面是index.css全局样式:

css 复制代码
* {
  margin: 0;
  padding: 0;
}

html,
body{
  width: 100%;
  height: 100%;
}

#root{
  width: 100%;
  height: 100%;
  background-color: antiquewhite;
}

/* 我觉得原生滚动条不好看 自定义一下滚动条 */
/* 滚动条整体部分 */
::-webkit-scrollbar {
  width: 8px;
  height: 6px;
}

/* 滚动条上方按钮 */
::-webkit-scrollbar-button {
  display: none;
}

/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
  background-color: rgb(191, 190, 190);
  border-radius: 10px;
}

/* 滚动条轨道 */
::-webkit-scrollbar-track {
  background-color: transparent;
}

App.css样式如下:

css 复制代码
.main-container{
  display: flex;
  height: 100%;
  width: 100%;
  justify-content: center;
  align-items: center;
}

.main-wrap{
  width: 40%;
  height: 90%;
  background-color: #fff;
  border-radius: 10px;
}

.note-title{
  display: flex;
  justify-content: center;
  align-items: center;
  height: 10%;
  width: 100%;
  color: #fff;
  background-color: #008cff;
  line-height: 100%;
  font-weight: 600;
  font-size: 32px;
  font-family: '方正姚体';
}

.item-body{
  width: 100%;
  height: 75%;
  overflow: auto;
}

.item-container{
  width: 100%;
  min-height: 20%;
}

.list-item{
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  height: 60px;
  list-style: none;
  background-color: #fff;
  padding-left: 10px;
  font-size: large;
  box-sizing: border-box;
  border-bottom: 1px dashed #008cff;
  transition: all .2s;
}

.list-item:hover{
  background-color: rgb(235, 235, 235);
}

.item-text{
  display: inline-block;
  width: 80%;
}

.finish-btn, .edit-btn{
  margin-right: 20px;
  cursor: pointer;
  width: 15%;
  height: 50%;
  border: 1px solid #008cff;
  outline: none;
  border-radius: 5px;
  background-color: transparent;
  color: #008cff;
  font-size: 16px;
  transition: all .2s;
}

.finish-btn:hover,
.edit-btn:hover,
.add-btn:hover{
  background-color: #008cff;
  color: #fff;
}

.container-footer{
  display: flex;
  justify-content: center;
  align-items: center;
  border-top: 1px dashed #008cff;
  width: 100%;
  height: 15%;
}

.add-btn{
  width: 60%;
  height: 55%;
  border: 2px solid #008cff;
  background-color: transparent;
  color: #008cff;
  font-size: 20px;
  font-weight: 600;
  font-family: '方正姚体';
  border-radius: 10px;
  transition: all .2s;
  cursor: pointer;
}

.no-data{
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-weight: 600;
  font-size: 26px;
  color: #008cff;
}

对应样式后,结构应为:

jsx 复制代码
<div className='main-container'>
  <div className='main-wrap'>
    <header className='note-title'>
      <span>待办事项</span>
    </header>
    <section className='item-body'>
      {
        list.length === 0 
          ? <span className='no-data'>暂无数据</span> 
          : 
          <ul className='item-container'>
            {
              list.map(item => {
                return (
                  <li key={item.id} className='list-item'>
                    <div className='line'></div>
                    <span className='item-text'>{item.thing}</span>
                    <button onClick={()=>handleEditClick(item)} className='edit-btn'>
                      编辑
                    </button>
                    <button onClick={(e)=>hanleFinishClick(item.id, e)} className='finish-btn'>
                      点击完成
                    </button>
                  </li>
                )
              })
            }
          </ul>
      }
    </section>
    <footer className='container-footer'>
      <button onClick={handlAddClick} className='add-btn'>{'+   添加事项'}</button>
    </footer>
  </div>
</div>

样式实现效果如下:

逻辑实现

使用state进行对数据的响应式操作,本项目主要涉及到对于数组的操作。

jsx 复制代码
  let [list, setList] = useState([]);

  function hanleFinishClick(id, e){ // 完成按钮事件
    // eslint-disable-next-line no-restricted-globals
    let will = confirm("确认完成事件吗?");
    if(will){
      // 将此项从数组中移除
      let newArr = list.filter(item=>item.id!==id);
      setList(newArr);
    }
  }

  function handlAddClick(){ // 添加按钮事件
    // prompt函数处理,也可以使用弹出层
    let thing = prompt("请输入待办事件:"); // 防止点击取消键数据变为null
    if(thing){
      let newList = [
        ...list,
        {
          id: nextId++,
          thing
        }
      ];
      setList(newList);
    }
  }

  function handleEditClick(item){
    let newthing = prompt("请输入待办事件:", item.thing);
    if(newthing){ // 防止点击取消键数据变为null
      let newList = list.map((i)=>{
        if(item.id === i.id){
          return {
            ...i,
            thing: newthing
          }
        }else{
          return i;
        }
      });
      setList(newList);
    }
  }

全部代码展示

jsx 复制代码
import './App.css';
import { useState } from 'react';

var nextId = 0;// 全局,放里面不对

function App() {
  let [list, setList] = useState([]);

  function hanleFinishClick(id, e){ // 完成按钮事件
    // eslint-disable-next-line no-restricted-globals
    let will = confirm("确认完成事件吗?");
    if(will){
      // 将此项从数组中移除
      let newArr = list.filter(item=>item.id!==id);
      setList(newArr);
    }
  }

  function handlAddClick(){ // 添加按钮事件
    // prompt函数处理,也可以使用弹出层
    let thing = prompt("请输入待办事件:"); // 防止点击取消键数据变为null
    if(thing){
      let newList = [
        ...list,
        {
          id: nextId++,
          thing
        }
      ];
      setList(newList);
    }
  }

  function handleEditClick(item){
    let newthing = prompt("请输入待办事件:", item.thing);
    if(newthing){ // 防止点击取消键数据变为null
      let newList = list.map((i)=>{
        if(item.id === i.id){
          return {
            ...i,
            thing: newthing
          }
        }else{
          return i;
        }
      });
      setList(newList);
    }
  }

  return (
    <div className='main-container'>
      <div className='main-wrap'>
        <header className='note-title'>
          <span>待办事项</span>
        </header>
        <section className='item-body'>
          {
            list.length === 0 
              ? <span className='no-data'>暂无数据</span> 
              : 
              <ul className='item-container'>
                {
                  list.map(item => {
                    return (
                      <li key={item.id} className='list-item'>
                        <div className='line'></div>
                        <span className='item-text'>{item.thing}</span>
                        <button onClick={()=>handleEditClick(item)} className='edit-btn'>
                          编辑
                        </button>
                        <button onClick={(e)=>hanleFinishClick(item.id, e)} className='finish-btn'>
                          点击完成
                        </button>
                      </li>
                    )
                  })
                }
              </ul>
          }
        </section>
        <footer className='container-footer'>
          <button onClick={handlAddClick} className='add-btn'>{'+   添加事项'}</button>
        </footer>
      </div>
    </div>
  );
}

export default App;

最终效果

初始:

添加:

成功,并点了编辑:

成功:

完成(删除):

成功:

改进方向

  1. 可以自己写一个小小的服务器,使得数据能够持久化存储而不是关了就没了。
  2. 可以将使用原生的prompt和confirm函数改为自己封装的组件或函数,以更美观更符合自己的要求。
  3. 可以拆分出多组件,使项目更为模块化,提高可维护性。
  4. 添加动画,使之不生硬,更为美观。
相关推荐
Find23 天前
MaxKB 集成langchain + Vue + PostgreSQL 的 本地大模型+本地知识库 构建私有大模型 | MarsCode AI刷题
青训营笔记
理tan王子23 天前
伴学笔记 AI刷题 14.数组元素之和最小化 | 豆包MarsCode AI刷题
青训营笔记
理tan王子23 天前
伴学笔记 AI刷题 25.DNA序列编辑距离 | 豆包MarsCode AI刷题
青训营笔记
理tan王子23 天前
伴学笔记 AI刷题 9.超市里的货物架调整 | 豆包MarsCode AI刷题
青训营笔记
夭要7夜宵25 天前
分而治之,主题分片Partition | 豆包MarsCode AI刷题
青训营笔记
三六1 个月前
刷题漫漫路(二)| 豆包MarsCode AI刷题
青训营笔记
tabzzz1 个月前
突破Zustand的局限性:与React ContentAPI搭配使用
前端·青训营笔记
Serendipity5651 个月前
Go 语言入门指南——单元测试 | 豆包MarsCode AI刷题;
青训营笔记
wml1 个月前
前端实践-使用React实现简单代办事项列表 | 豆包MarsCode AI刷题
青训营笔记
用户44710308932421 个月前
详解前端框架中的设计模式 | 豆包MarsCode AI刷题
青训营笔记