功能需求:
-
添加待办事项
-
点击任务完成/未完成切换
-
删除任务
目标:掌握组件、props、useState、useEffect、事件处理、本地存储保存数据(localStorage)
🧱 第一步:初始化项目【动手试试才能掌握!】
✅ 如果你使用的是 命令行+VSCode,请在终端中运行:
npx create-react-app todo-app
cd todo-app
npm start
这会创建一个新的 React 项目,并在浏览器中打开开发环境。
📁 第二步:项目结构规划
我们把功能分为几个部分:
App.jsx:主组件TodoInput.jsx:输入框组件TodoList.jsx:展示任务列表TodoItem.jsx:单个任务组件
🧑💻 第三步:开始编写代码
🔹 1. 编辑 App.jsx(主逻辑)
import React, { useState } from 'react';
import TodoInput from './components/TodoInput';
import TodoList from './components/TodoList';
function App() {
const [todos, setTodos] = useState([]);
const addTodo = (text) => {
const newTodo = { id: Date.now(), text, completed: false };
setTodos([newTodo, ...todos]);
};
const toggleTodo = (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="container">
<h1>我的待办事项</h1>
<TodoInput onAdd={addTodo} />
<TodoList todos={todos} onToggle={toggleTodo} onDelete={deleteTodo} />
</div>
);
}
export default App;
🔹 2. 创建 components/TodoInput.jsx
import React, { useState } from 'react';
function TodoInput({ onAdd }) {
const [text, setText] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (text.trim() === '') return;
onAdd(text);
setText('');
};
return (
<form onSubmit={handleSubmit}>
<input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="添加任务..."
/>
<button type="submit">添加</button>
</form>
);
}
export default TodoInput;
🔹 3. 创建 components/TodoList.jsx
import React from 'react';
import TodoItem from './TodoItem';
function TodoList({ todos, onToggle, onDelete }) {
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={onToggle}
onDelete={onDelete}
/>
))}
</ul>
);
}
export default TodoList;
🔹 4. 创建 components/TodoItem.jsx
import React from 'react';
function TodoItem({ todo, onToggle, onDelete }) {
return (
<li
style={{
textDecoration: todo.completed ? 'line-through' : 'none',
cursor: 'pointer'
}}
>
<span onClick={() => onToggle(todo.id)}>{todo.text}</span>
<button onClick={() => onDelete(todo.id)}>删除</button>
</li>
);
}
export default TodoItem;
🎨 简单样式建议(可选)
在 index.css 或 App 的外层添加一点样式:
.container {
max-width: 500px;
margin: 50px auto;
text-align: center;
}
input {
padding: 10px;
width: 70%;
margin-right: 10px;
}
button {
padding: 10px;
}