引言
Redux 曾是 React 状态管理的标准解决方案,但传统 Redux 的样板代码过多、配置复杂、异步处理繁琐。Redux Toolkit(RTK)官方推出,旨在解决这些痛点,成为现代 Redux 开发的标准方式。
一、为什么选择 Redux Toolkit?
传统 Redux 的三大痛点
- 配置复杂:需要手动配置 store、中间件、devtools
- 样板代码多:action types、action creators、reducers 分离
- 异步处理 麻烦:需要额外安装 redux-thunk 或 redux-saga
RTK 的优势
| 特性 | 传统 Redux | Redux Toolkit |
|---|---|---|
| Store 配置 | 多行手动配置 | configureStore 一行搞定 |
| Action 定义 | 手动定义 type + creator | createSlice 自动生成 |
| 不可变更新 | 需要 spread 运算符 | Immer 内置,可直接修改 |
| DevTools | 手动配置 | 自动集成 |
| TypeScript | 繁琐的类型定义 | 内置类型支持 |
二、四步实战 Redux Toolkit
步骤 1:安装与 Store 配置
bash
npm install @reduxjs/toolkit react-redux
javascript
// src/app/store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import userReducer from '../features/user/userSlice';
export const store = configureStore({
reducer: {
counter: counterReducer,
user: userReducer,
},
});
export default store;
configureStore 自动帮你:
- 配置 Redux DevTools
- 设置 redux-thunk 中间件
- 启用 Immer 支持不可变更新
步骤 2:创建 Slice
javascript
// src/features/counter/counterSlice.js
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
history: [],
},
reducers: {
increment: (state) => {
// Immer 允许直接修改 state
state.value += 1;
state.history.push(`+1 → ${state.value}`);
},
decrement: (state) => {
state.value -= 1;
state.history.push(`-1 → ${state.value}`);
},
incrementByAmount: (state, action) => {
state.value += action.payload;
state.history.push(`+${action.payload} → ${state.value}`);
},
reset: (state) => {
state.value = 0;
state.history = [];
},
},
});
export const { increment, decrement, incrementByAmount, reset } = counterSlice.actions;
export default counterSlice.reducer;
createSlice 自动生成:
- Action types(
counter/increment) - Action creators(
increment()) - Reducer 函数
步骤 3:处理异步请求
javascript
// src/features/user/userSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
// 创建异步 thunk
export const fetchUser = createAsyncThunk(
'user/fetchUser',
async (userId, { rejectWithValue }) => {
try {
const response = await axios.get(`/api/users/${userId}`);
return response.data;
} catch (error) {
return rejectWithValue(error.response.data);
}
}
);
const userSlice = createSlice({
name: 'user',
initialState: {
data: null,
loading: false,
error: null,
},
reducers: {
clearUser: (state) => {
state.data = null;
state.error = null;
},
},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchUser.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
});
},
});
export const { clearUser } = userSlice.actions;
export default userSlice.reducer;
createAsyncThunk 自动生成三种状态:
pending:请求中fulfilled:请求成功rejected:请求失败
步骤 4:React 组件中使用
javascript
// src/components/Counter.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, incrementByAmount, reset } from '../features/counter/counterSlice';
function Counter() {
const count = useSelector((state) => state.counter.value);
const history = useSelector((state) => state.counter.history);
const dispatch = useDispatch();
return (
<div className="counter">
<h2>计数器:{count}</h2>
<div className="buttons">
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
<button onClick={() => dispatch(reset())}>重置</button>
</div>
{history.length > 0 && (
<div className="history">
<h3>操作历史:</h3>
<ul>
{history.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
)}
</div>
);
}
export default Counter;
javascript
// src/components/UserProfile.jsx
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchUser, clearUser } from '../features/user/userSlice';
function UserProfile({ userId }) {
const dispatch = useDispatch();
const { data, loading, error } = useSelector((state) => state.user);
useEffect(() => {
dispatch(fetchUser(userId));
}, [userId, dispatch]);
if (loading) return <div>加载中...</div>;
if (error) return <div>错误:{error}</div>;
if (!data) return null;
return (
<div className="user-profile">
<h2>{data.name}</h2>
<p>邮箱:{data.email}</p>
<button onClick={() => dispatch(clearUser())}>清除</button>
</div>
);
}
export default UserProfile;
三、项目结构组织
bash
src/
├── app/
│ └── store.js # Store 配置
├── features/
│ ├── counter/
│ │ ├── counterSlice.js
│ │ └── Counter.jsx
│ └── user/
│ ├── userSlice.js
│ └── UserProfile.jsx
├── components/ # 通用组件
└── index.js # 入口文件
javascript
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './app/store';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')).render(
<Provider store={store}>
<App />
</Provider>
);
四、最佳实践
1. 使用 Typed Hooks(TypeScript 项目)
typescript
// src/app/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';
// 在整个应用中使用这些 hooks,而非默认的 useDispatch/useSelector
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
2. Slice 文件组织
- 每个功能模块一个 slice
- 相关的组件和 slice 放在同一目录
- 使用
export统一导出 actions 和 reducer
3. 避免的常见错误
| 错误做法 | 正确做法 |
|---|---|
| 直接修改 state 外部 | 只在 reducer 内修改 |
| 在 slice 中发异步请求 | 使用 createAsyncThunk |
| 过多全局状态 | 局部状态用 useState |
忽略 extraReducers |
正确处理异步状态 |
总结
Redux Toolkit 让 Redux 开发变得简单高效:
configureStore- 一键配置 storecreateSlice- 自动生成 action + reducercreateAsyncThunk- 优雅处理异步- Immer 内置 - 直接修改 state 无副作用
选型建议:
- 小型应用 → Context 或 Zustand
- 中大型应用 → Redux Toolkit
- 需要时间旅行调试 → Redux Toolkit