如何使用 React Hooks 实现复杂组件的状态管理
状态管理的基础 - useState Hook
useState 是最常用的 Hook 之一,它可以让我们在函数式组件中定义状态并可在组件中进行更新。下面是一个简单的例子:
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1);
}
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
在上面的代码中,我们使用 useState 定义了一个 count 状态,并通过 setCount 方法更新它。每次点击 Increment 按钮时,count 的值都会增加 1。
状态管理的进阶 - useReducer Hook
当组件的状态比较复杂时,使用 useState 可能会导致代码变得混乱。这时候,可以考虑使用 useReducer。useReducer 是另一种状态管理的方式,它可以让我们更好地组织代码,并将状态更新的逻辑放在 reducer 函数内。
import { useReducer } from "react";
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>Increment</button>
<button onClick={() => dispatch({ type: "decrement" })}>Decrement</button>
</div>
);
}
在上面的代码中,我们使用 useReducer 定义了一个状态 count,并将状态更新的逻辑放在了 reducer 函数内。每次点击 Increment 或 Decrement 按钮时,都会触发对应的 action。
实战 - 使用 React Hooks 实现 TodoList
在上面介绍了 useState 和 useReducer,下面我们来实现一个 TodoList 组件。这个组件有以下功能:
- 显示所有的 todo 项
- 添加新的 todo 项
- 删除已经完成的 todo 项
首先,我们需要定义一个 TodoItem 组件,用于显示每一项的 todo。
function TodoItem({ text, completed, onClick }) {
return (
<li
style={{ textDecoration: completed ? "line-through" : "none" }}
onClick={onClick}
>
{text}
</li>
);
}
接下来,我们可以开始编写 TodoList 组件。由于我们需要管理多个 todo 项的状态,所以使用 useReducer 更为合适。
import { useReducer } from "react";
import TodoItem from "./TodoItem";
const ADD_TODO = "ADD_TODO";
const TOGGLE_TODO = "TOGGLE_TODO";
const DELETE_TODO = "DELETE_TODO";
function reducer(state, action) {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
id: state.length + 1,
text: action.text,
completed: false,
},
];
case TOGGLE_TODO:
return state.map((todo) =>
todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
);
case DELETE_TODO:
return state.filter((todo) => todo.id !== action.id);
default:
throw new Error();
}
}
export default function TodoList() {
const [state, dispatch] = useReducer(reducer, []);
function handleAdd(text) {
dispatch({ type: ADD_TODO, text });
}
function handleToggle(id) {
dispatch({ type: TOGGLE_TODO, id });
}
function handleDelete(id) {
dispatch({ type: DELETE_TODO, id });
}
return (
<div>
<ul>
{state.map((todo) => (
<TodoItem
key={todo.id}
text={todo.text}
completed={todo.completed}
onClick={() => handleToggle(todo.id)}
/>
))}
</ul>
<input
type="text"
placeholder="Add a new todo"
onKeyDown={(e) => e.key === "Enter" && handleAdd(e.target.value)}
/>
<button onClick={() => handleDelete()}>Delete Completed Todos</button>
</div>
);
}
在上面的代码中,我们定义了三个对应不同操作的 action 类型,并在 reducer 函数中处理相应的逻辑。在 TodoList 组件中,我们通过 useReducer 定义了 todo 状态,并提供了三个方法 handleAdd、handleToggle 和 handleDelete,用于添加、切换和删除 todo 项。同时,我们也渲染了一个 input 元素和一个 button 元素,分别用于添加新的 todo 项和删除已完成的 todo 项。
到这里,我们就实现了一个简单的 TodoList 组件。使用 useReducer 可以让我们更好地组织代码,并且使我们的代码更加清晰易懂。