用Create-React-App搭建TODO List必须包含:
✅ 自定义Hook实现持久化存储
✅ Context API 跨组件状态管理
Todo List 的详细实现步骤:
一、项目初始化
1. 创建项目
npx create-react-app todo-list-context
cd todo-list-context
2. 清理默认文件
删除 src 目录下不必要的文件(如 App.test.js, logo.svg 等),仅保留:
src/
├── App.js
├── index.js
└── styles.css (新建)
二、核心代码实现
1. 创建自定义 Hook useLocalStorage(持久化存储)
新建文件 src/hooks/useLocalStorage.js:
import { useState, useEffect } from 'react';
export function useLocalStorage(key, initialValue) {
// 从 localStorage 读取初始值
const [value, setValue] = useState(() => {
const storedValue = localStorage.getItem(key);
return storedValue ? JSON.parse(storedValue) : initialValue;
});
// 当 value 变化时更新 localStorage
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
2. 创建 Context 状态管理
新建文件 src/contexts/TodoContext.js:
import { createContext, useContext } from 'react';
import { useLocalStorage } from '../hooks/useLocalStorage';
const TodoContext = createContext();
export function TodoProvider({ children }) {
// 使用自定义 Hook 持久化 todos
const [todos, setTodos] = useLocalStorage('todos', []);
// 添加 Todo
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text, completed: false }]);
};
// 切换完成状态
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
// 删除 Todo
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
// 全选/全不选
const toggleAll = (completed) => {
setTodos(todos.map(todo => ({ ...todo, completed })));
};
return (
<TodoContext.Provider value={{ todos, addTodo, toggleTodo, deleteTodo, toggleAll }}>
{children}
</TodoContext.Provider>
);
}
export function useTodos() {
return useContext(TodoContext);
}
3. 主组件 App.js
import { TodoProvider } from './contexts/TodoContext';
import TodoList from './components/TodoList';
import AddTodo from './components/AddTodo';
import ToggleAll from './components/ToggleAll';
import './styles.css';
export default function App() {
return (
<TodoProvider>
<div className="app">
<h1>Todo List</h1>
<AddTodo />
<ToggleAll />
<TodoList />
</div>
</TodoProvider>
);
}
4. 子组件实现
(1) AddTodo.js(添加新 Todo)
import { useState } from 'react';
import { useTodos } from '../contexts/TodoContext';
export default function AddTodo() {
const [text, setText] = useState('');
const { addTodo } = useTodos();
const handleSubmit = (e) => {
e.preventDefault();
if (text.trim()) {
addTodo(text.trim());
setText('');
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Add a new todo..."
/>
<button type="submit">Add</button>
</form>
);
}
(2) TodoList.js(显示 Todo 列表)
import { useTodos } from '../contexts/TodoContext';
import TodoItem from './TodoItem';
export default function TodoList() {
const { todos } = useTodos();
return (
<ul className="todo-list">
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
);
}
(3) TodoItem.js(单个 Todo 项)
import { useTodos } from '../contexts/TodoContext';
export default function TodoItem({ todo }) {
const { toggleTodo, deleteTodo } = useTodos();
return (
<li className={todo.completed ? 'completed' : ''}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
);
}
(4) ToggleAll.js(全选/全不选)
import { useTodos } from '../contexts/TodoContext';
export default function ToggleAll() {
const { todos, toggleAll } = useTodos();
const allCompleted = todos.every(todo => todo.completed);
return (
<div className="toggle-all">
<input
type="checkbox"
checked={allCompleted}
onChange={() => toggleAll(!allCompleted)}
/>
<label>Toggle All</label>
</div>
);
}
5. 样式文件 styles.css
.app {
max-width: 600px;
margin: 20px auto;
padding: 20px;
}
.todo-list {
list-style: none;
padding: 0;
}
.todo-list li {
display: flex;
align-items: center;
padding: 10px;
border-bottom: 1px solid #eee;
}
.todo-list li.completed span {
text-decoration: line-through;
color: #999;
}
input[type="text"] {
width: 70%;
padding: 8px;
margin-right: 5px;
}
button {
padding: 8px 12px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
.toggle-all {
margin: 10px 0;
}
三、运行与测试
1. 启动项目
npm start
2. 功能验证
- ✅ 添加新 Todo
- ✅ 切换单个/全部完成状态
- ✅ 删除 Todo
- ✅ 刷新页面后数据保留(持久化验证)
四、项目结构总结
src/
├── components/
│ ├── AddTodo.js
│ ├── TodoList.js
│ ├── TodoItem.js
│ └── ToggleAll.js
├── contexts/
│ └── TodoContext.js
├── hooks/
│ └── useLocalStorage.js
├── App.js
├── index.js
└── styles.css
五、技术要点
- 自定义 Hook
useLocalStorage封装了 localStorage 的读写逻辑,实现状态持久化。 - Context API
TodoProvider包裹整个应用,提供全局状态useTodos方便子组件获取状态和方法
- 组件拆分
将 UI 拆分为AddTodo、TodoList、TodoItem等可复用组件 - 单向数据流
通过 Context 传递状态和操作方法,避免 prop drilling
这个实现方案完整覆盖了 React Hooks、Context API 和持久化存储的核心知识点,适合作为学习模板。
1383

被折叠的 条评论
为什么被折叠?



