开发一个任务记录网站(To-Do List)是 React 的经典入门项目,它涵盖了组件化、状态管理、用户交互等核心概念。以下是详细的实现思路和步骤:
一、功能需求分析
一个基础的任务记录网站通常包含以下功能:
- 添加任务:输入任务名称并添加到列表。
- 查看任务列表:显示所有任务。
- 标记完成/未完成:切换任务的完成状态。
- 删除任务:移除不再需要的任务。
- (可选)编辑任务:修改任务内容。
- (可选)筛选任务:按“全部”、“已完成”、“未完成”过滤。
二、技术选型与工具
- 框架:React
- 状态管理:
useStateHook(足够满足此项目) - 路由:
react-router-dom(如果有多页面,如主页、归档页) - 样式:CSS Modules / Tailwind CSS / Styled-components 等
- 构建工具:Create React App 或 Vite
三、组件结构设计(组件化)
将 UI 拆分为可复用的组件:
App
├── Header (标题)
├── AddTodo (添加任务表单)
├── TodoList (任务列表容器)
│ └── TodoItem (单个任务项)
└── Footer (筛选器、统计信息)
四、状态(State)设计
在 App 组件中定义核心状态:
const [todos, setTodos] = useState([
{ id: 1, text: '学习 React', completed: false },
{ id: 2, text: '写 To-Do 应用', completed: true }
]);
const [filter, setFilter] = useState('all'); // 'all', 'active', 'completed'
todos: 存储任务数组,每个任务对象包含id,text,completed属性。filter: 当前的筛选状态。
五、核心功能实现
1. 添加任务 (AddTodo 组件)
function AddTodo({ onAdd }) {
const [input, setInput] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (input.trim() !== '') {
// 调用父组件传递的回调函数
onAdd(input);
setInput(''); // 清空输入框
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="添加新任务..."
/>
<button type="submit">添加</button>
</form>
);
}
App 组件传递 onAdd 回调:
function handleAdd(text) {
setTodos(prevTodos => [
...prevTodos,
{ id: Date.now(), text, completed: false }
]);
}
2. 显示和操作任务列表 (TodoList 和 TodoItem)
// TodoItem 组件
function TodoItem({ todo, onToggle, onDelete, onEdit }) {
const [isEditing, setIsEditing] = useState(false);
const [editText, setEditText] = useState(todo.text);
const handleSave = () => {
onEdit(todo.id, editText.trim());
setIsEditing(false);
};
if (isEditing) {
return (
<li>
<input
value={editText}
onChange={(e) => setEditText(e.target.value)}
onBlur={handleSave}
onKeyPress={(e) => e.key === 'Enter' && handleSave()}
autoFocus
/>
</li>
);
}
return (
<li>
<span
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
onClick={() => onToggle(todo.id)}
>
{todo.text}
</span>
<button onClick={() => setIsEditing(true)}>编辑</button>
<button onClick={() => onDelete(todo.id)}>删除</button>
</li>
);
}
// TodoList 组件
function TodoList({ todos, onToggle, onDelete, onEdit }) {
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={onToggle}
onDelete={onDelete}
onEdit={onEdit}
/>
))}
</ul>
);
}
App 组件提供回调函数:
const handleToggle = (id) => {
setTodos(prevTodos =>
prevTodos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
const handleDelete = (id) => {
setTodos(prevTodos => prevTodos.filter(todo => todo.id !== id));
};
const handleEdit = (id, newText) => {
if (newText === '') return;
setTodos(prevTodos =>
prevTodos.map(todo =>
todo.id === id ? { ...todo, text: newText } : todo
)
);
};
3. 筛选任务 (Footer 组件)
function Footer({ filter, onFilterChange }) {
const filters = ['all', 'active', 'completed'];
return (
<div>
{filters.map(f => (
<button
key={f}
style={{ fontWeight: filter === f ? 'bold' : 'normal' }}
onClick={() => onFilterChange(f)}
>
{f === 'all' ? '全部' : f === 'active' ? '未完成' : '已完成'}
</button>
))}
</div>
);
}
根据 filter 状态计算要显示的任务:
const filteredTodos = todos.filter(todo => {
if (filter === 'active') return !todo.completed;
if (filter === 'completed') return todo.completed;
return true; // 'all'
});
六、数据持久化(进阶)
默认情况下,刷新页面后数据会丢失。可以使用 localStorage 进行简单持久化。
// 在 App 组件中
useEffect(() => {
const savedTodos = localStorage.getItem('todos');
if (savedTodos) {
setTodos(JSON.parse(savedTodos));
}
}, []);
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);
七、总结:实现思路
- 拆分组件:将 UI 分解为独立、可复用的小组件。
- 提升状态:确定哪些数据是共享的,将其提升到共同的父组件(通常是
App)中管理。 - 单向数据流:通过
props将数据和回调函数从父组件传递给子组件。 - 响应用户交互:在事件处理函数中调用
setState来更新状态,触发重新渲染。 - 关注点分离:
AddTodo负责输入,TodoItem负责单个任务的展示和操作,Footer负责筛选。 - 不可变性:更新数组或对象状态时,创建新的副本,而不是直接修改。
这个项目虽然简单,但完整地体现了 React 的核心思想:组件化、声明式 UI、单向数据流和状态驱动视图。掌握它,就掌握了 React 开发的基础范式。
THE END


