Redux Toolkit 實(shí)戰(zhàn)小結(jié)
引言
Redux 曾是 React 狀態(tài)管理的標(biāo)準(zhǔn)解決方案,但傳統(tǒng) Redux 的樣板代碼過多、配置復(fù)雜、異步處理繁瑣。Redux Toolkit(RTK)官方推出,旨在解決這些痛點(diǎn),成為現(xiàn)代 Redux 開發(fā)的標(biāo)準(zhǔn)方式。
一、為什么選擇 Redux Toolkit?
傳統(tǒng) Redux 的三大痛點(diǎn)
- 配置復(fù)雜:需要手動(dòng)配置 store、中間件、devtools
- 樣板代碼多:action types、action creators、reducers 分離
- 異步處理 麻煩:需要額外安裝 redux-thunk 或 redux-saga
RTK 的優(yōu)勢(shì)
| 特性 | 傳統(tǒng) Redux | Redux Toolkit |
|---|---|---|
| Store 配置 | 多行手動(dòng)配置 | configureStore 一行搞定 |
| Action 定義 | 手動(dòng)定義 type + creator | createSlice 自動(dòng)生成 |
| 不可變更新 | 需要 spread 運(yùn)算符 | Immer 內(nèi)置,可直接修改 |
| DevTools | 手動(dòng)配置 | 自動(dòng)集成 |
| TypeScript | 繁瑣的類型定義 | 內(nèi)置類型支持 |
二、四步實(shí)戰(zhàn) Redux Toolkit
步驟 1:安裝與 Store 配置
npm install @reduxjs/toolkit react-redux
// 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 自動(dòng)幫你:
- 配置 Redux DevTools
- 設(shè)置 redux-thunk 中間件
- 啟用 Immer 支持不可變更新
步驟 2:創(chuàng)建 Slice
// 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 自動(dòng)生成:
- Action types(
counter/increment) - Action creators(
increment()) - Reducer 函數(shù)
步驟 3:處理異步請(qǐng)求
// src/features/user/userSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
// 創(chuàng)建異步 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 自動(dòng)生成三種狀態(tài):
pending:請(qǐng)求中fulfilled:請(qǐng)求成功rejected:請(qǐng)求失敗
步驟 4:React 組件中使用
// 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>計(jì)數(shù)器:{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;// 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>錯(cuò)誤:{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;三、項(xiàng)目結(jié)構(gòu)組織
src/ ├── app/ │ └── store.js # Store 配置 ├── features/ │ ├── counter/ │ │ ├── counterSlice.js │ │ └── Counter.jsx │ └── user/ │ ├── userSlice.js │ └── UserProfile.jsx ├── components/ # 通用組件 └── index.js # 入口文件
// 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>
);四、最佳實(shí)踐
1. 使用 Typed Hooks(TypeScript 項(xiàng)目)
// src/app/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';
// 在整個(gè)應(yīng)用中使用這些 hooks,而非默認(rèn)的 useDispatch/useSelector
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;2. Slice 文件組織
- 每個(gè)功能模塊一個(gè) slice
- 相關(guān)的組件和 slice 放在同一目錄
- 使用
export統(tǒng)一導(dǎo)出 actions 和 reducer
3. 避免的常見錯(cuò)誤
| 錯(cuò)誤做法 | 正確做法 |
|---|---|
| 直接修改 state 外部 | 只在 reducer 內(nèi)修改 |
| 在 slice 中發(fā)異步請(qǐng)求 | 使用 createAsyncThunk |
| 過多全局狀態(tài) | 局部狀態(tài)用 useState |
| 忽略 extraReducers | 正確處理異步狀態(tài) |
總結(jié)
Redux Toolkit 讓 Redux 開發(fā)變得簡(jiǎn)單高效:
- configureStore - 一鍵配置 store
- createSlice - 自動(dòng)生成 action + reducer
- createAsyncThunk - 優(yōu)雅處理異步
- Immer 內(nèi)置 - 直接修改 state 無副作用
選型建議:
- 小型應(yīng)用 → Context 或 Zustand
- 中大型應(yīng)用 → Redux Toolkit
- 需要時(shí)間旅行調(diào)試 → Redux Toolkit
到此這篇關(guān)于Redux Toolkit 實(shí)戰(zhàn)小結(jié)的文章就介紹到這了,更多相關(guān)Redux Toolkit 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Redux?Toolkit的基本使用示例詳解(Redux工具包)
- 如何使用Redux Toolkit簡(jiǎn)化Redux
- React immer與Redux Toolkit使用教程詳解
- React?TypeScript?應(yīng)用中便捷使用Redux?Toolkit方法詳解
- 一文詳解ReactNative狀態(tài)管理redux-toolkit使用
- 如何使用Redux Toolkit簡(jiǎn)化Redux
- 使用React和Redux Toolkit實(shí)現(xiàn)用戶登錄功能
- React使用Redux Toolkit的方法示例
- React?中使用?Redux?Toolkit?狀態(tài)管理的實(shí)踐
相關(guān)文章
React Native可復(fù)用 UI分離布局組件和狀態(tài)組件技巧
這篇文章主要為大家介紹了React Native可復(fù)用 UI分離布局組件和狀態(tài)組件使用技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
關(guān)于React狀態(tài)管理的三個(gè)規(guī)則總結(jié)
隨著 JavaScript 單頁應(yīng)用開發(fā)日趨復(fù)雜,JavaScript 需要管理比任何時(shí)候都要多的 state (狀態(tài)),這篇文章主要給大家介紹了關(guān)于React狀態(tài)管理的三個(gè)規(guī)則,需要的朋友可以參考下2021-07-07
react路由跳轉(zhuǎn)傳參刷新頁面后參數(shù)丟失的解決
這篇文章主要介紹了react路由跳轉(zhuǎn)傳參刷新頁面后參數(shù)丟失的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
react實(shí)現(xiàn)動(dòng)態(tài)選擇框
這篇文章主要為大家詳細(xì)介紹了react實(shí)現(xiàn)動(dòng)態(tài)選擇框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
React.InputHTMLAttributes實(shí)踐和注意事項(xiàng)
文章討論了如何在封裝組件中使用React.InputHTMLAttributes,以及如何通過 {...inputProps} 動(dòng)態(tài)傳遞屬性,最后,文章總結(jié)了使用React.InputHTMLAttributes的最佳實(shí)踐和注意事項(xiàng),感興趣的朋友一起看看吧2024-11-11
React中的Context應(yīng)用場(chǎng)景分析
這篇文章主要介紹了React中的Context應(yīng)用場(chǎng)景分析,Context 提供了一種在組件之間共享數(shù)據(jù)的方式,而不必顯式地通過組件樹的逐層傳遞 props,通過實(shí)例代碼給大家介紹使用步驟,感興趣的朋友跟隨小編一起看看吧2021-06-06
react-router-dom6(對(duì)比?router5)快速入門指南
這篇文章主要介紹了快速上手react-router-dom6(對(duì)比?router5),通過本文學(xué)習(xí)最新的react-router-dom?v6版本的路由知識(shí),并且會(huì)與v5老版本進(jìn)行一些對(duì)比,需要的朋友可以參考下2022-08-08

