在 React 中,useReducer
是一个用于管理复杂状态逻辑的 Hook,它类似于 Redux 中的 reducer 模式,但更轻量且适用于组件内部或结合 Context API 实现全局状态管理。以下是 useReducer
的详细用法指南:
1. 基本语法
javascript
const [state, dispatch] = useReducer(reducer, initialState);
reducer
:一个函数,接收当前状态和 action,返回新状态。initialState
:状态的初始值。state
:当前状态。dispatch
:用于触发状态更新的函数(发送 action)。
2. 核心概念
(1) Reducer 函数
Reducer 的格式:(state, action) => newState
action
:通常是一个对象,包含type
(操作类型)和可选的payload
(数据)。- 必须返回新状态(不可直接修改原状态!)。
javascript
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
case 'SET_VALUE':
return { ...state, value: action.payload };
default:
return state; // 默认返回原状态
}
}
(2) Dispatch Action
通过 dispatch
发送 action 来更新状态:
javascript
dispatch({ type: 'INCREMENT' });
dispatch({ type: 'SET_VALUE', payload: 42 });
3. 完整示例
计数器组件
jsx
import { useReducer } from 'react';
// 定义 reducer
function counterReducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
case 'RESET':
return { count: 0 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
<button onClick={() => dispatch({ type: 'RESET' })}>Reset</button>
</div>
);
}
4. 进阶用法
(1) 结合 Payload 传递数据
javascript
dispatch({ type: 'SET_NAME', payload: 'Alice' });
// Reducer 处理
case 'SET_NAME':
return { ...state, name: action.payload };
(2) 惰性初始化
如果初始状态需要计算,可以传入一个初始化函数:
javascript
function init(initialCount) {
return { count: initialCount };
}
const [state, dispatch] = useReducer(reducer, initialCount, init);
(3) 结合 Context API 实现全局状态
jsx
// 创建 Context
const CounterContext = createContext();
// Provider 组件
function CounterProvider({ children }) {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<CounterContext.Provider value={{ state, dispatch }}>
{children}
</CounterContext.Provider>
);
}
// 子组件中使用
function ChildComponent() {
const { state, dispatch } = useContext(CounterContext);
// ...
}
5. 与 useState
的对比
场景 | useState |
useReducer |
---|---|---|
简单状态 | 适合(如布尔值、数字) | 过度设计 |
复杂状态逻辑 | 代码冗长(需多个 useState ) |
适合(逻辑集中管理) |
依赖前一个状态 | 需用函数更新(setCount(c => c + 1) ) |
自动处理(reducer 接收当前状态) |
跨组件共享状态 | 需结合 Context | 更适合(与 Context 天然搭配) |
6. 最佳实践
-
拆分 Reducer
如果逻辑复杂,可以按功能拆分成多个 reducer,再用
combineReducers
(类似 Redux):javascriptfunction rootReducer(state, action) { return { counter: counterReducer(state.counter, action), user: userReducer(state.user, action), }; }
-
避免深层嵌套状态
尽量扁平化状态结构,便于更新。
-
异步操作
在
dispatch
外处理异步逻辑(如 API 请求),完成后调用dispatch
:javascriptasync function fetchData(dispatch) { const data = await api.get(); dispatch({ type: 'SET_DATA', payload: data }); }
7. 示例:Todo 列表
jsx
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return [...state, { text: action.payload, completed: false }];
case 'TOGGLE_TODO':
return state.map((todo, index) =>
index === action.payload
? { ...todo, completed: !todo.completed }
: todo
);
default:
return state;
}
}
function TodoApp() {
const [todos, dispatch] = useReducer(todoReducer, []);
const [input, setInput] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
dispatch({ type: 'ADD_TODO', payload: input });
setInput('');
};
return (
<div>
<form onSubmit={handleSubmit}>
<input value={input} onChange={(e) => setInput(e.target.value)} />
</form>
<ul>
{todos.map((todo, index) => (
<li
key={index}
onClick={() => dispatch({ type: 'TOGGLE_TODO', payload: index })}
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
>
{todo.text}
</li>
))}
</ul>
</div>
);
}
总结
- 何时用
useReducer
?
状态逻辑复杂、需要依赖前一个状态、或需要跨组件共享状态时。 - 优势:逻辑集中、易于测试、适合与 Context 结合。
- 注意 :避免过度使用,简单场景直接用
useState
更高效。