前端实践选题 使用 React 实现一个简单的待办事项列表 | 青训营

97 阅读3分钟

前言

使用 React 实现一个简单的待办事项列表:用户可以添加、编辑和删除待办事项。这个项目是众多前端实践选题我第一个想做的,因为它不像其它的选题需要探讨总结,我还是更加喜欢亲自写出代码。

I like "Talk is cheap, show me the code"

本是想先让这个 TodoList 的 UI,功能都做的差不多了才开始写这篇文章,但我发现这样不便于记录我在这其中遇到的困难与解决方法。而对于目前大三即将准备考研的我,估计是要放弃 Web 开发一段时间,许多在此次项目学到的东西可能都会被遗忘,所以本篇文章采取实时更新的模式。

前端构建工具

毫无疑问当然是本次青训营的课程重点讲述的前端构建工具 —— Vite

Lesson 11 Vite 知识体系 学习笔记 | 青训营

设计思路

image.png

Feature now:

  1. 通过 editBtn 开启编辑模式,所有文字改动全部在原处进行, 通过再次点击 editBtn 进行保存
  2. 每条 item 都有一个唯一 id,用于索引删除,用 React 来实现 id 的自增和唯一

Todo:

  1. 可以导出 TodoItem 为 JSON 文件,可以读取 JSON 文件快速添加 TodoItem
  2. 增加闹钟提醒
  3. ……

在做之前,我想了很多的功能,力图想一次做到最好,可是后来发现真的很难,而且对于我目前纯纯小白来说很多我想要的功能都无法实现。我曾困于很久,一直没有coding。

但我发现这对于我一个练习项目,我需要做很多功能,甚至是吸收市面上所有 TodoList 的优秀功能吗?答案肯定是否定的,这也许就是练习,也许就是学生时代的“试错成本比较低吧”。

这个 TodoList 我更倾向于练习,可能有许多用不到的功能(真的会有人通过JSON文件导入item吗?🤣)但是对于我来说这是一件很酷的事,在此之间也学到了许多。

newFunctionUpdateProcess

To be update……

Code

首先是封装 TodoItem 的代码

import React, { useState } from "react";

function TodoItem({itemTitleVar, itemLocationVar, itemTimeVar, editBoolVar}){
    const [itemTitle, setItemTitle] = useState(itemTitleVar);
    const [itemLocation, setItemLocation]= useState(itemLocationVar);
    const [itemTime, setItemTime] = useState(itemTimeVar);
    const [editBool, setEditBool] = useState(editBoolVar);
    var tempItemTitle = itemTitle;
    var tempItemLocation = itemLocation;
    var tempItemTime = itemTime;

    return (
        <div>
            <div className="itemTitleDiv">
                <strong>itemTitle: </strong>
                <h1 className="itemTitleContent" contentEditable={editBool} onInput={(e) => {tempItemTitle = e.target.innerText}} >{itemTitle}</h1>
            </div>

            <div className="itemLocationDiv">
                <strong>itemLocation: </strong>
                <div className="itemLocationContent" contentEditable={editBool} onInput={(e) => {tempItemLocation = e.target.innerText}}>{itemLocation}</div>
            </div>

            <div className="itemTimeDiv">
                <strong>itemTime: </strong>
                <div className="itemTimeContent" contentEditable={editBool} onInput={(e) => {tempItemTime = e.target.innerText}}>{itemTime}</div>
            </div>

            <div className="editBoolDiv">
                {editBool ? (
                    <label id="editBtn" onClick={() => {
                        setEditBool(!editBool);
                        setItemTitle(tempItemTitle);
                        setItemLocation(tempItemLocation);
                        setItemTime(tempItemTime);
                      }}>✔️</label>
                    ) : (
                    <label id="editBtn" onClick={() => setEditBool(!editBool)}>🖊️</label>
                )}

            </div>
            <div className="true-state">
                <h5>Var Value:</h5><br></br>
                {itemTitle}<br></br>
                {itemLocation}<br></br>
                {itemTime}<br></br>
            </div>


        </div>
    )

}

export default TodoItem;

接下来是渲染 TodoList 的代码

import React, { useState } from 'react';
import TodoItem from './TodoItem';

const TodoList = () => {

  const [todos, setTodos] = useState([
    { id: 1, itemTitleVar: 'Buy groceries', itemLocationVar: 'Supermarket', itemTimeVar: '10:00 AM', editBoolVar: true },
    { id: 2, itemTitleVar: 'Finish project', itemLocationVar: 'Home', itemTimeVar: '3:00 PM', editBoolVar: false },
  ]);

  const [nextId, setNextId] = useState(3);

  const addTodo = () => {
    const newTodo = {
      id: nextId, 
      itemTitleVar: 'Clike 🖊️ to change content',
      itemLocationVar: 'Home',
      itemTimeVar: '5:00 PM',
      editBoolVar: false
    };
    setTodos([...todos, newTodo]);
    setNextId(nextId + 1);
  }

  const deleteTodo = (id) => {
    setTodos(prev => {
      return prev.filter(todo => todo.id !== id); 
    });
  }

  
  return (
    <div>
      <h1>Todo List</h1>
      <button id="addTodoBtn" onClick={addTodo}>➕️</button>
      {todos.map((todo) => (
        <div className="todo-item">
          <TodoItem
          key={todo.id}
          id={todo.id}
          itemTitleVar={todo.itemTitleVar}
          itemLocationVar={todo.itemLocationVar}
          itemTimeVar={todo.itemTimeVar}
          editBoolVar={todo.editBoolVar}
        />
        <button onClick={() => deleteTodo(todo.id)}>❌</button>
        </div>
        
      ))}
      
    </div>
  );
};

export default TodoList;

最后是样式

.todo-item {
    background-color: #ffffff;
    border: 1px solid #e0e0e0;
    box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
    border-radius: 8px;
    padding: 16px;
    margin: 16px;
    transition: transform 0.2s ease-in-out;
    background-color: lightblue;
    /* Optional: Hover effect */
    &:hover {
      transform: translateY(-4px);
      box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
    } 
}

#editBtn {
  font-size: 2rem;
  float: right;
}

body{
  background-color: antiquewhite;
}

#addTodoBtn{
  background-color: transparent;
  border: 1px solid transparent; /* 设置边框为透明 */
}

实际展示效果

功能完好

image.png