useReducer 与 Redux 对比
分析 useReducer 和 Redux 的区别,以及各自适用场景
问题
useReducer 能代替 Redux 吗?它们有什么区别?
解答
简短回答
不能完全代替。useReducer 只解决了状态管理的一部分问题,Redux 提供了完整的状态管理方案。
useReducer 基本用法
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 };
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>
</div>
);
}
useReducer + Context 实现跨组件状态共享
import { createContext, useContext, useReducer } from 'react';
// 创建 Context
const StoreContext = createContext(null);
// reducer
function appReducer(state, action) {
switch (action.type) {
case 'SET_USER':
return { ...state, user: action.payload };
case 'ADD_TODO':
return { ...state, todos: [...state.todos, action.payload] };
default:
return state;
}
}
// Provider 组件
function StoreProvider({ children }) {
const [state, dispatch] = useReducer(appReducer, {
user: null,
todos: []
});
return (
<StoreContext.Provider value={{ state, dispatch }}>
{children}
</StoreContext.Provider>
);
}
// 自定义 Hook
function useStore() {
const context = useContext(StoreContext);
if (!context) {
throw new Error('useStore must be used within StoreProvider');
}
return context;
}
// 使用
function UserProfile() {
const { state, dispatch } = useStore();
return (
<div>
<p>User: {state.user?.name}</p>
<button onClick={() => dispatch({
type: 'SET_USER',
payload: { name: 'John' }
})}>
Login
</button>
</div>
);
}
功能对比
| 功能 | useReducer | Redux |
|---|---|---|
| 状态管理 | ✅ | ✅ |
| 跨组件共享 | 需配合 Context | ✅ 内置 |
| 中间件 | ❌ | ✅ |
| 异步处理 | 手动实现 | redux-thunk/saga |
| DevTools | ❌ | ✅ |
| 时间旅行调试 | ❌ | ✅ |
| 状态持久化 | 手动实现 | redux-persist |
| 性能优化 | 需手动处理 | 内置 selector |
Redux Toolkit 示例
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useSelector, useDispatch } from 'react-redux';
// 创建 slice(包含 reducer 和 actions)
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
increment: (state) => {
state.count += 1; // RTK 使用 Immer,可以直接修改
},
decrement: (state) => {
state.count -= 1;
},
incrementByAmount: (state, action) => {
state.count += action.payload;
}
}
});
// 导出 actions
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// 创建 store
const store = configureStore({
reducer: {
counter: counterSlice.reducer
}
});
// 组件中使用
function Counter() {
// useSelector 自动订阅状态变化,只在相关状态改变时重渲染
const count = useSelector((state) => state.counter.count);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
</div>
);
}
// App
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
何时选择 useReducer
// 1. 组件内部复杂状态
function Form() {
const [state, dispatch] = useReducer(formReducer, {
name: '',
email: '',
errors: {},
isSubmitting: false
});
// 状态逻辑集中在 reducer 中,组件更清晰
}
// 2. 状态更新逻辑复杂
function reducer(state, action) {
switch (action.type) {
case 'SUBMIT_START':
return { ...state, isSubmitting: true, errors: {} };
case 'SUBMIT_SUCCESS':
return { ...state, isSubmitting: false, data: action.payload };
case 'SUBMIT_ERROR':
return { ...state, isSubmitting: false, errors: action.payload };
// 多个 action 修改多个字段,用 reducer 更清晰
}
}
何时选择 Redux
// 1. 需要中间件处理异步
const fetchUser = (id) => async (dispatch) => {
dispatch({ type: 'FETCH_START' });
try {
const user = await api.getUser(id);
dispatch({ type: 'FETCH_SUCCESS', payload: user });
} catch (error) {
dispatch({ type: 'FETCH_ERROR', payload: error });
}
};
// 2. 需要 DevTools 调试
// Redux DevTools 可以查看每个 action、状态变化、时间旅行
// 3. 大型应用,多个模块共享状态
const store = configureStore({
reducer: {
user: userReducer,
products: productsReducer,
cart: cartReducer,
orders: ordersReducer
}
});
关键点
- useReducer 是组件级方案:适合单组件或小范围状态管理
- Redux 是应用级方案:提供中间件、DevTools、状态持久化等完整生态
- useReducer + Context 有性能问题:Context 值变化会导致所有消费者重渲染
- Redux 的 useSelector 自带性能优化:只在选中的状态变化时重渲染
- 小项目用 useReducer:简单、无依赖、够用
- 大项目用 Redux Toolkit:功能完整、调试方便、生态成熟
目录