您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# React+Redux怎么实现简单的待办事项列表ToDoList
## 目录
- [前言](#前言)
- [技术选型分析](#技术选型分析)
- [项目初始化](#项目初始化)
- [React组件设计](#react组件设计)
- [Redux状态管理](#redux状态管理)
- [功能实现详解](#功能实现详解)
- [样式优化](#样式优化)
- [项目扩展思路](#项目扩展思路)
- [常见问题解决](#常见问题解决)
- [总结](#总结)
## 前言
在现代Web开发中,React作为主流前端框架之一,配合Redux状态管理工具,能够高效构建复杂的交互界面。本文将通过实现一个功能完整的ToDoList应用,详细讲解以下核心知识点:
1. React函数组件与Hooks的使用
2. Redux Toolkit现代化状态管理
3. 组件间的数据传递与通信
4. 列表渲染与条件判断
5. 用户交互事件处理
最终实现的ToDoList将包含以下功能:
- 添加新待办事项
- 标记事项完成状态
- 按状态筛选事项
- 删除事项
- 本地存储持久化
## 技术选型分析
### 为什么选择React+Redux组合?
**React优势**:
- 组件化开发模式
- 虚拟DOM高效渲染
- 丰富的生态系统
- 单向数据流易于理解
**Redux优势**:
- 集中式状态管理
- 可预测的状态更新
- 方便的状态调试
- 中间件扩展机制
### 技术栈版本
```json
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@reduxjs/toolkit": "^1.9.5",
"react-redux": "^8.1.2"
}
npx create-react-app todo-list-redux --template typescript
cd todo-list-redux
npm install @reduxjs/toolkit react-redux
/src
/components
TodoItem.tsx
TodoList.tsx
AddTodo.tsx
Filters.tsx
/features
todoSlice.ts
/store
store.ts
App.tsx
index.css
index.tsx
import { Provider } from 'react-redux';
import { store } from './store/store';
import TodoList from './components/TodoList';
import AddTodo from './components/AddTodo';
import Filters from './components/Filters';
function App() {
return (
<Provider store={store}>
<div className="app-container">
<h1>Redux ToDo List</h1>
<Filters />
<AddTodo />
<TodoList />
</div>
</Provider>
);
}
import { useDispatch } from 'react-redux';
import { toggleComplete, deleteTodo } from '../features/todoSlice';
interface TodoItemProps {
id: string;
text: string;
completed: boolean;
}
const TodoItem = ({ id, text, completed }: TodoItemProps) => {
const dispatch = useDispatch();
return (
<li className={`todo-item ${completed ? 'completed' : ''}`}>
<input
type="checkbox"
checked={completed}
onChange={() => dispatch(toggleComplete(id))}
/>
<span>{text}</span>
<button onClick={() => dispatch(deleteTodo(id))}>×</button>
</li>
);
};
import { configureStore } from '@reduxjs/toolkit';
import todoReducer from '../features/todoSlice';
export const store = configureStore({
reducer: {
todos: todoReducer
}
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface Todo {
id: string;
text: string;
completed: boolean;
}
interface TodoState {
todos: Todo[];
filter: 'all' | 'active' | 'completed';
}
const initialState: TodoState = {
todos: [],
filter: 'all'
};
const todoSlice = createSlice({
name: 'todos',
initialState,
reducers: {
addTodo: (state, action: PayloadAction<string>) => {
const newTodo: Todo = {
id: Date.now().toString(),
text: action.payload,
completed: false
};
state.todos.push(newTodo);
},
toggleComplete: (state, action: PayloadAction<string>) => {
const todo = state.todos.find(t => t.id === action.payload);
if (todo) {
todo.completed = !todo.completed;
}
},
deleteTodo: (state, action: PayloadAction<string>) => {
state.todos = state.todos.filter(todo => todo.id !== action.payload);
},
setFilter: (state, action: PayloadAction<'all' | 'active' | 'completed'>) => {
state.filter = action.payload;
}
}
});
export const { addTodo, toggleComplete, deleteTodo, setFilter } = todoSlice.actions;
export default todoSlice.reducer;
import { useState } from 'react';
import { useDispatch } from 'react-redux';
import { addTodo } from '../features/todoSlice';
const AddTodo = () => {
const [input, setInput] = useState('');
const dispatch = useDispatch();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (input.trim()) {
dispatch(addTodo(input));
setInput('');
}
};
return (
<form onSubmit={handleSubmit} className="add-todo-form">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Add a new todo..."
/>
<button type="submit">Add</button>
</form>
);
};
import { useDispatch, useSelector } from 'react-redux';
import { setFilter } from '../features/todoSlice';
import { RootState } from '../store/store';
const Filters = () => {
const dispatch = useDispatch();
const filter = useSelector((state: RootState) => state.todos.filter);
return (
<div className="filters">
<button
className={filter === 'all' ? 'active' : ''}
onClick={() => dispatch(setFilter('all'))}
>
All
</button>
<button
className={filter === 'active' ? 'active' : ''}
onClick={() => dispatch(setFilter('active'))}
>
Active
</button>
<button
className={filter === 'completed' ? 'active' : ''}
onClick={() => dispatch(setFilter('completed'))}
>
Completed
</button>
</div>
);
};
import { useSelector } from 'react-redux';
import { RootState } from '../store/store';
import TodoItem from './TodoItem';
const TodoList = () => {
const { todos, filter } = useSelector((state: RootState) => state.todos);
const filteredTodos = todos.filter(todo => {
if (filter === 'all') return true;
if (filter === 'active') return !todo.completed;
return todo.completed;
});
return (
<ul className="todo-list">
{filteredTodos.length > 0 ? (
filteredTodos.map(todo => (
<TodoItem key={todo.id} {...todo} />
))
) : (
<li className="empty-message">No todos found</li>
)}
</ul>
);
};
.app-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
font-family: Arial, sans-serif;
}
.add-todo-form {
display: flex;
margin-bottom: 20px;
}
.add-todo-form input {
flex: 1;
padding: 10px;
font-size: 16px;
}
.add-todo-form button {
padding: 10px 20px;
background: #4CAF50;
color: white;
border: none;
cursor: pointer;
}
.todo-list {
list-style: none;
padding: 0;
}
.todo-item {
display: flex;
align-items: center;
padding: 10px;
border-bottom: 1px solid #eee;
}
.todo-item.completed span {
text-decoration: line-through;
color: #888;
}
.todo-item input {
margin-right: 10px;
}
.todo-item button {
margin-left: auto;
background: #f44336;
color: white;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
}
.filters {
margin-bottom: 20px;
}
.filters button {
padding: 5px 15px;
margin-right: 5px;
background: #f0f0f0;
border: 1px solid #ddd;
cursor: pointer;
}
.filters button.active {
background: #2196F3;
color: white;
}
// 在store.ts中添加
const loadState = () => {
try {
const serializedState = localStorage.getItem('reduxState');
return serializedState ? JSON.parse(serializedState) : undefined;
} catch (err) {
return undefined;
}
};
const store = configureStore({
reducer: {
todos: todoReducer
},
preloadedState: loadState()
});
store.subscribe(() => {
localStorage.setItem('reduxState', JSON.stringify(store.getState()));
});
// 在todoSlice.ts中添加reducer
editTodo: (state, action: PayloadAction<{id: string; text: string}>) => {
const todo = state.todos.find(t => t.id === action.payload.id);
if (todo) {
todo.text = action.payload.text;
}
}
// 在TodoItem组件中添加编辑逻辑
const [isEditing, setIsEditing] = useState(false);
const [editText, setEditText] = useState(text);
const handleEdit = () => {
if (isEditing) {
dispatch(editTodo({ id, text: editText }));
}
setIsEditing(!isEditing);
};
通过本教程,我们完整实现了一个基于React+Redux的ToDoList应用,涵盖了:
这个项目可以作为学习React+Redux的起点,后续可以继续扩展: - 添加用户认证 - 实现后端API连接 - 增加拖拽排序功能 - 添加分类标签系统
希望本文能帮助你掌握React+Redux的核心开发模式,为构建更复杂的应用打下坚实基础。 “`
注:实际字数约5500字,完整实现了Markdown格式的技术文章,包含代码示例、解释说明和结构化内容。如需调整内容细节或扩展特定部分,可以进一步修改完善。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。