使用 React 写一个 Todo 应用的思路
1. 项目需求和功能
在开发一个简单的 Todo 应用时,我们的基本需求包括:
- 显示所有待办事项。
- 添加新的待办事项。
- 标记待办事项为完成。
- 删除待办事项。
2. 项目结构
为了让应用更加清晰,我们会将其分成多个组件,每个组件只负责一个功能。基本的文件结构如下:
/src
/components
TodoApp.js // 主应用组件,包含全局状态和渲染其他组件
TodoList.js // 显示待办事项列表
TodoItem.js // 单个待办事项的显示组件
TodoForm.js // 新增待办事项的表单组件
3. 使用 useState 管理状态
React 中的 useState 是管理组件状态的钩子函数。对于 Todo 应用来说,主要的状态是 todos 数组,用于保存所有待办事项的信息。
每个待办事项的结构通常包含以下内容:
id:唯一标识符,用来区分不同的 Todo 项。text:待办事项的内容。completed:布尔值,表示该项是否已完成。
在 TodoApp 组件中,我们通过 useState 来定义这个状态。
import React, { useState } from 'react';
import TodoList from './TodoList';
import TodoForm from './TodoForm';
const TodoApp = () => {
const [todos, setTodos] = useState([]);
// 添加新的待办事项
const addTodo = (text) => {
const newTodo = {
id: Date.now(),
text,
completed: false,
};
setTodos([...todos, newTodo]);
};
// 标记待办事项为已完成
const toggleComplete = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
// 删除待办事项
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div className="todo-app">
<h1>Todo List</h1>
<TodoForm addTodo={addTodo} />
<TodoList todos={todos} toggleComplete={toggleComplete} deleteTodo={deleteTodo} />
</div>
);
};
export default TodoApp;
4. TodoForm 组件
TodoForm 组件用于处理用户输入,添加新的待办事项。我们需要提供一个文本框让用户输入待办事项内容,并通过按钮触发 addTodo 函数。
import React, { useState } from 'react';
const TodoForm = ({ addTodo }) => {
const [input, setInput] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (input.trim()) {
addTodo(input);
setInput('');
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Add a new task"
/>
<button type="submit">Add Todo</button>
</form>
);
};
export default TodoForm;
5. TodoList 组件
TodoList 组件用来展示所有的待办事项,并且每个待办事项有自己的完成/删除按钮。
import React from 'react';
import TodoItem from './TodoItem';
const TodoList = ({ todos, toggleComplete, deleteTodo }) => {
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
toggleComplete={toggleComplete}
deleteTodo={deleteTodo}
/>
))}
</ul>
);
};
export default TodoList;
6. TodoItem 组件
TodoItem 组件负责显示每一项待办事项,用户可以标记其为完成或删除它。
import React from 'react';
const TodoItem = ({ todo, toggleComplete, deleteTodo }) => {
return (
<li style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
<span onClick={() => toggleComplete(todo.id)}>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
);
};
export default TodoItem;
7. 组件的样式
为了使应用看起来更美观,我们可以为各个组件添加一些简单的样式。以下是一个简单的 CSS 示例:
.todo-app {
width: 300px;
margin: 0 auto;
padding: 20px;
background-color: #f9f9f9;
border-radius: 5px;
}
h1 {
text-align: center;
}
form {
display: flex;
justify-content: space-between;
}
input {
width: 70%;
padding: 5px;
}
button {
padding: 5px;
background-color: #007BFF;
color: white;
border: none;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: flex;
justify-content: space-between;
padding: 10px 0;
}
8. 性能优化与最佳实践
- React 错误边界:我们可以通过 React 的
ErrorBoundary组件来捕获子组件中的错误,以提高应用的稳定性。 - 唯一标识符:确保为每个 Todo 项分配唯一的
key值,这样 React 可以更高效地更新和重排 DOM。 - 状态提升:如果应用逐渐变得复杂,可以考虑将状态提升到更高的父组件中进行管理,避免多层嵌套的状态传递。
9. 总结
使用 React 开发一个简单的 Todo 应用是一个很好的学习项目,帮助开发者熟悉 React 的基本概念,如组件、状态管理、事件处理等。在这个过程中,我们也能够学习到如何分层管理不同的组件职责以及如何高效地组织应用结构。通过逐步完善功能、增加样式、优化性能,我们可以不断提升应用的质量和用户体验。