React 实现待办事项列表并扩展状态管理
在现代前端开发中,状态管理是构建复杂应用程序的核心部分。对于一个简单的待办事项列表,可以通过 React 的本地状态(useState)来管理。然而,在大型项目中,随着状态的复杂化和组件间通信的增多,管理状态变得更加困难。通过引入状态管理工具(如 Redux 或 Context API),可以更高效地管理全局状态,并使代码更具扩展性和可维护性。
本文将从一个简单的待办事项列表入手,展示如何通过本地状态实现功能,并逐步引入 Redux 和 Context API 扩展状态管理,探讨大型项目中的最佳实践。
一、使用 React 本地状态实现基础功能
1. 项目需求
实现一个待办事项列表,包含以下功能:
• 添加新任务。
• 标记任务完成状态。
• 删除任务。
2. React 本地状态实现
以下是使用 useState 的基本实现。
代码实现:
import React, { useState } from "react";
function TodoApp() {
const [tasks, setTasks] = useState([]);
const [newTask, setNewTask] = useState("");
const addTask = () => {
if (newTask.trim() === "") return;
setTasks([...tasks, { id: Date.now(), text: newTask, completed: false }]);
setNewTask("");
};
const toggleTask = (id) => {
setTasks(
tasks.map((task) =>
task.id === id ? { ...task, completed: !task.completed } : task
)
);
};
const deleteTask = (id) => {
setTasks(tasks.filter((task) => task.id !== id));
};
return (
Todo List
<input
type="text"
value={newTask}
onChange={(e) => setNewTask(e.target.value)}
/>
<button onClick={addTask}>Add Task
{tasks.map((task) => (
<li key={task.id}>
<span
style={{
textDecoration: task.completed ? "line-through" : "none",
}}
onClick={() => toggleTask(task.id)}
>
{task.text}
<button onClick={() => deleteTask(task.id)}>Delete
))}
);
}
export default TodoApp;
3. 分析基础实现
在上述代码中:
-
useState 用于管理任务列表和输入框内容的状态。
-
通过数组的增删改操作实现任务的添加、完成状态切换和删除功能。
局限性:
• 当组件层级加深时,状态的共享变得困难。
• 如果需要将任务列表的状态共享给多个组件,会产生“状态提升”和复杂的 props 传递。
二、扩展状态管理:使用 Context API
1. Context API 的介绍
React 的 Context API 是一种轻量级的状态管理工具,适用于共享全局状态的场景。它通过 Context.Provider 提供状态,通过 useContext 消费状态,避免了组件间复杂的 props 传递。
2. 改造代码
将任务状态提取到 Context 中,并通过 Provider 提供状态和操作方法。
代码实现:
任务状态 Context:
import React, { createContext, useContext, useState } from "react";
// 创建 Context
const TaskContext = createContext();
// 提供者组件
export const TaskProvider = ({ children }) => {
const [tasks, setTasks] = useState([]);
const addTask = (text) => {
if (text.trim() === "") return;
setTasks([...tasks, { id: Date.now(), text, completed: false }]);
};
const toggleTask = (id) => {
setTasks(
tasks.map((task) =>
task.id === id ? { ...task, completed: !task.completed } : task
)
);
};
const deleteTask = (id) => {
setTasks(tasks.filter((task) => task.id !== id));
};
return (
<TaskContext.Provider value={{ tasks, addTask, toggleTask, deleteTask }}>
{children}
</TaskContext.Provider>
);
};
// 使用 Context 的自定义钩子
export const useTasks = () => useContext(TaskContext);
待办事项组件:
import React, { useState } from "react";
import { TaskProvider, useTasks } from "./TaskContext";
function TaskInput() {
const [newTask, setNewTask] = useState("");
const { addTask } = useTasks();
const handleAdd = () => {
addTask(newTask);
setNewTask("");
};
return (
<input
type="text"
value={newTask}
onChange={(e) => setNewTask(e.target.value)}
/>
<button onClick={handleAdd}>Add Task
);
}
function TaskList() {
const { tasks, toggleTask, deleteTask } = useTasks();
return (
{tasks.map((task) => (
<li key={task.id}>
<span
style={{
textDecoration: task.completed ? "line-through" : "none",
}}
onClick={() => toggleTask(task.id)}
>
{task.text}
<button onClick={() => deleteTask(task.id)}>Delete
))}
);
}
function TodoApp() {
return (
Todo List with Context API
);
}
export default TodoApp;
3. 分析 Context API 的改造
- 状态共享:
• TaskProvider 作为状态提供者,可以共享状态给所有子组件,避免了复杂的 props 传递。
- 自定义钩子:
• useTasks 简化了组件中状态和方法的调用。
适用场景:
• 状态相对简单且集中时,Context API 是理想选择。
局限性:
• 状态更新时,所有使用该 Context 的组件都会重新渲染,可能影响性能。
三、扩展状态管理:使用 Redux
1. Redux 的介绍
Redux 是一个流行的 JavaScript 状态管理工具,适用于复杂的应用程序。它通过全局状态树存储状态,并通过 dispatch 和 action更新状态,提供可预测的状态管理。
2. Redux 改造代码
将任务状态管理迁移到 Redux。
Redux 配置:
Actions:
export const addTask = (text) => ({
type: "ADD_TASK",
payload: text,
});
export const toggleTask = (id) => ({
type: "TOGGLE_TASK",
payload: id,
});
export const deleteTask = (id) => ({
type: "DELETE_TASK",
payload: id,
});
Reducer:
const initialState = {
tasks: [],
};
export const taskReducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_TASK":
return {
...state,
tasks: [
...state.tasks,
{ id: Date.now(), text: action.payload, completed: false },
],
};
case "TOGGLE_TASK":
return {
...state,
tasks: state.tasks.map((task) =>
task.id === action.payload
? { ...task, completed: !task.completed }
: task
),
};
case "DELETE_TASK":
return {
...state,
tasks: state.tasks.filter((task) => task.id !== action.payload),
};
default:
return state;
}
};
Store 配置:
import { createStore } from "redux";
import { taskReducer } from "./reducers";
export const store = createStore(taskReducer);
React 组件:
通过 react-redux 连接组件。
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { addTask, toggleTask, deleteTask } from "./actions";
function TodoApp() {
const tasks = useSelector((state) => state.tasks);
const dispatch = useDispatch();
const handleAdd = (text) => {
dispatch(addTask(text));
};
return (
Todo List with Redux
<input type="text" onBlur={(e) => handleAdd(e.target.value)} />
-
<span
style={{
textDecoration: task.completed ? "line-through" : "none",
}}
onClick={() => dispatch(toggleTask(task.id))}
>
{task.text}
</
{tasks.map((task) => (