引言
待办事项列表(To-Do List)是一个经典的前端练习项目,可以很好地帮助开发者理解和应用 React 的核心概念。它不仅涵盖了状态管理、事件处理等基础知识,还能让开发者体会到组件化设计的优势。在本次实践中,我将逐步实现一个具备 添加、编辑 和 删除 功能的待办事项列表。为了更贴近实际需求,我还会结合自己的理解,分享一些设计和实现过程中的思考。
设计思路
在正式编码前,我们需要对功能需求和设计架构进行清晰的规划,确保代码逻辑简洁、易维护。
1. 确定功能需求
待办事项列表的核心功能包括:
- 添加新任务:用户可以输入待办事项的内容并点击按钮将其加入列表。
- 编辑任务:允许用户修改已经添加的待办事项内容。
- 删除任务:用户可以移除已完成或不再需要的事项。
这些功能覆盖了用户管理待办事项的基本需求,是一个功能完整的小应用。
2. 分解功能模块
React 鼓励开发者将页面划分为多个功能单一的组件。这种组件化设计能使代码更加清晰,同时提升复用性。按照功能需求,将项目拆解为以下主要组件:
-
App组件
- 作为应用的入口,负责状态管理和功能逻辑的调度。
-
TodoForm组件
- 提供用户输入的表单,用于添加和编辑待办事项。
-
TodoList组件
- 用于展示所有待办事项的列表。
-
TodoItem组件
- 用于展示单个待办事项及其操作按钮(如编辑、删除)。
3. 确定数据结构
为了便于管理和操作,每个待办事项可以设计成一个对象,包含以下属性:
const todo = {
id: 1, // 唯一标识符
text: "学习React", // 待办事项内容
completed: false // 是否完成
};
所有的待办事项会存储在一个数组中,应用状态的更新操作将基于此数组进行。
4. 交互流程设计
- 添加任务:在输入框中输入任务内容,点击“添加”按钮,输入的内容会追加到待办列表。
- 编辑任务:点击某任务旁边的“编辑”按钮,触发输入框内容的替换,修改后点击保存。
- 删除任务:点击“删除”按钮,即可移除对应任务。
基于此设计,我们可以清晰地描绘应用的交互路径。
实现步骤
1. 初始化项目
通过 Vite 快速搭建 React 项目:
npm create vite@latest todo-app --template react
cd todo-app
npm install
启动开发服务器:
npm run dev
2. 编写基础结构
App.jsx 是应用的主组件,管理待办事项数组的状态,并通过 props 将功能传递给子组件。
import React, { useState } from "react";
import TodoForm from "./TodoForm";
import TodoList from "./TodoList";
function App() {
const [todos, setTodos] = useState([]);
// 添加任务
const addTodo = (text) => {
const newTodo = { id: Date.now(), text, completed: false };
setTodos([...todos, newTodo]);
};
// 编辑任务
const editTodo = (id, newText) => {
const updatedTodos = todos.map(todo =>
todo.id === id ? { ...todo, text: newText } : todo
);
setTodos(updatedTodos);
};
// 删除任务
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div>
<h1>待办事项列表</h1>
<TodoForm onAdd={addTodo} />
<TodoList todos={todos} onEdit={editTodo} onDelete={deleteTodo} />
</div>
);
}
export default App;
3. 添加TodoForm组件
TodoForm 组件负责处理用户输入的新任务内容,并通过 props 调用父组件的添加逻辑。
import React, { useState } from "react";
function TodoForm({ onAdd }) {
const [input, setInput] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
if (input.trim()) {
onAdd(input); // 调用父组件传递的添加方法
setInput(""); // 清空输入框
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="添加新的待办事项"
/>
<button type="submit">添加</button>
</form>
);
}
export default TodoForm;
4. 编写TodoList和TodoItem组件
TodoList 组件接收 todos 数据并渲染子组件 TodoItem。
import React from "react";
import TodoItem from "./TodoItem";
function TodoList({ todos, onEdit, onDelete }) {
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onEdit={onEdit}
onDelete={onDelete}
/>
))}
</ul>
);
}
export default TodoList;
TodoItem 组件负责展示单个任务的内容,并处理编辑和删除操作:
import React, { useState } from "react";
function TodoItem({ todo, onEdit, onDelete }) {
const [isEditing, setIsEditing] = useState(false);
const [newText, setNewText] = useState(todo.text);
const handleEdit = () => {
setIsEditing(true);
};
const handleSave = () => {
onEdit(todo.id, newText);
setIsEditing(false);
};
return (
<li>
{isEditing ? (
<>
<input
type="text"
value={newText}
onChange={(e) => setNewText(e.target.value)}
/>
<button onClick={handleSave}>保存</button>
</>
) : (
<>
<span>{todo.text}</span>
<button onClick={handleEdit}>编辑</button>
</>
)}
<button onClick={() => onDelete(todo.id)}>删除</button>
</li>
);
}
export default TodoItem;
结果展示
- 添加待办
- 编辑待办
- 删除待办
个人思考
-
关于组件化
将代码划分为多个小型组件后,每个组件的职责单一且明确,这样不仅易于调试,还方便功能扩展。例如,如果我们未来想加入任务完成状态的功能,只需在TodoItem中调整。 -
状态管理的局限性
当前我们用useState管理应用状态,这种方式适合小型项目。若状态变得复杂或需要跨组件共享,推荐使用 Context 或 Redux 进行全局状态管理。 -
用户体验优化
- 可以增加本地存储功能(如
localStorage),让用户刷新页面后仍能保留数据。 - 增加任务完成标记功能,用户可以一目了然地看到哪些任务已完成。
- 提供更美观的样式或动画,增强用户的交互体验。
- 可以增加本地存储功能(如
结语
通过这个项目,我们熟悉了 React 的基本操作,同时深刻体会到组件化和状态管理的重要性。未来的改进方向可以包括更复杂的功能逻辑或与后端联通,构建一个功能更完善的任务管理工具。