Redux Toolkit 是如何使用 Redux 的
什麼是 Redux Toolkit?
Redux Toolkit(簡稱 「RTK」)是我們建議用於撰寫 Redux 邏輯的官方推薦方法。@reduxjs/toolkit
套件包覆核心 redux
套件,並包含我們認為對於建置 Redux 應用程式至關重要的 API 方法和常見依賴項。Redux Toolkit 建置於我們建議的最佳實務中,簡化大多數 Redux 任務、防止常見錯誤,並讓撰寫 Redux 應用程式變得更容易。
如果您今天撰寫任何 Redux 邏輯,您應該使用 Redux Toolkit 來撰寫該程式碼!
RTK 包含實用程式,有助於簡化許多常見用例,包括 商店設定、建立 Reducer 和撰寫不可變更新邏輯,甚至 一次建立整個「狀態切片」。
無論您是設定第一個專案的新手 Redux 使用者,還是想要簡化現有應用程式的經驗豐富使用者,Redux Toolkit 都可以協助您改善 Redux 程式碼。
請參閱這些頁面,以了解如何將「現代 Redux」與 Redux Toolkit 搭配使用
- 「Redux Essentials」教學課程,它會教導如何使用 Redux Toolkit,以正確的方式使用 Redux,以應付實際世界的應用程式
- Redux Fundamentals,第 8 部分:使用 Redux Toolkit 的現代 Redux,它會展示如何將教學課程較早部分的低階範例轉換為現代 Redux Toolkit 等效項
- 使用 Redux:移轉到現代 Redux,它會說明如何將不同類型的舊版 Redux 邏輯移轉到現代 Redux 等效項
Redux Toolkit 與 Redux Core 有何不同
什麼是「Redux」?
首先要問的是,「什麼是 Redux?」
Redux 實際上是
- 包含「全域」狀態的單一儲存體
- 當應用程式中發生某事時,將純粹的物件動作傳送至儲存體
- 純粹的簡化函式,會檢視這些動作並傳回不可變的更新狀態
雖然不是必需的,但 您的 Redux 程式碼通常也包含
- 產生這些動作物件的動作建立器
- 啟用副作用的中介軟體
- 包含具有副作用的同步或非同步邏輯的 Thunk 函式
- 正規化的狀態,以啟用透過 ID 查詢項目
- 使用 Reselect 函式庫的記憶化選擇器函式,以最佳化衍生資料
- Redux DevTools Extension,以檢視您的動作歷程和狀態變更
- 動作、狀態和其他函式的 TypeScript 類型
此外,Redux 通常與 React-Redux 函式庫搭配使用,讓您的 React 元件可以與 Redux 儲存體通訊。
Redux 核心做了什麼事?
Redux 核心是一個非常小且故意不提供意見的函式庫。它提供一些小型的 API 原語
createStore
用於實際建立 Redux 儲存體combineReducers
用於將多個切片簡化器結合為一個更大的簡化器applyMiddleware
用於將多個中間件結合為一個儲存體增強器compose
用於將多個儲存體增強器結合為一個單一的儲存體增強器
除此之外,應用程式中所有其他與 Redux 相關的邏輯都必須完全由您撰寫。
好消息是,這表示 Redux 可以 以許多不同的方式使用。壞消息是,沒有任何輔助工具可以讓您的程式碼更容易撰寫。
例如,簡化器函式只是一個函式。在 Redux Toolkit 之前,您通常會使用 switch
陳述式和手動更新來撰寫簡化器。您可能還會手動撰寫動作建立器和動作類型常數
const ADD_TODO = 'ADD_TODO'
const TODO_TOGGLED = 'TODO_TOGGLED'
export const addTodo = (text) => ({
type: ADD_TODO,
payload: { text, id: nanoid() },
})
export const todoToggled = (id) => ({
type: TODO_TOGGLED,
payload: { id },
})
export const todosReducer = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return state.concat({
id: action.payload.id,
text: action.payload.text,
completed: false,
})
case TODO_TOGGLED:
return state.map((todo) => {
if (todo.id !== action.payload.id) return todo
return {
...todo,
completed: !todo.completed,
}
})
default:
return state
}
}
這些程式碼沒有特別依賴於 redux
核心函式庫的任何 API。但是,這有很多程式碼要撰寫。不可變更新需要大量手寫物件擴充和陣列運算,而且很容易出錯並在過程中意外變異狀態(始終是 Redux 錯誤的頭號原因!)。儘管不是絕對必要,但將一個功能的程式碼分散到多個檔案中(例如 actions/todos.js
、constants/todos.js
和 reducers/todos.js
)也很常見。
此外,儲存體設定通常需要一系列步驟才能新增常用的中間件(例如 thunk)並啟用 Redux DevTools Extension 支援,即使這些是幾乎每個 Redux 應用程式中使用的標準工具。
Redux Toolkit 做了什麼事?
雖然這些是 Redux 文件中最初顯示的模式,但不幸的是,它們需要大量非常冗長且重複的程式碼。大部分的樣板程式碼不需要使用 Redux。最重要的是,樣板程式碼會導致更多犯錯的機會。
我們特別建立 Redux Toolkit 來消除手寫 Redux 邏輯中的「樣板程式碼」,防止常見錯誤,並提供簡化標準 Redux 任務的 API.
Redux Toolkit 從兩個關鍵的 API 開始,簡化你在每個 Redux 應用程式中執行最常見的工作
configureStore
設定一個設定良好的 Redux 儲存體,使用單一函式呼叫,包括結合 reducer、新增 thunk 中介軟體,並設定 Redux DevTools 整合。它也比createStore
更容易設定,因為它採用具名稱的選項參數。createSlice
讓你撰寫使用 Immer 函式庫 的 reducer,以使用「變異」JS 語法(例如state.value = 123
)來啟用撰寫不可變更新,無需任何擴充。它也會自動為每個 reducer 產生動作建立函式,並根據 reducer 的名稱在內部產生動作類型字串。最後,它與 TypeScript 相容良好。
這表示你撰寫的程式碼可以大幅簡化。例如,相同的 todos reducer 可以只是
import { createSlice } from '@reduxjs/toolkit'
const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
todoAdded(state, action) {
state.push({
id: action.payload.id,
text: action.payload.text,
completed: false,
})
},
todoToggled(state, action) {
const todo = state.find((todo) => todo.id === action.payload)
todo.completed = !todo.completed
},
},
})
export const { todoAdded, todoToggled } = todosSlice.actions
export default todosSlice.reducer
所有動作建立程式和動作類型都是自動產生的,而 reducer 程式碼較短且容易理解。在每種情況下實際更新的內容也更清楚。
使用 configureStore
,儲存體設定可以簡化為
import { configureStore } from '@reduxjs/toolkit'
import todosReducer from '../features/todos/todosSlice'
import filtersReducer from '../features/filters/filtersSlice'
export const store = configureStore({
reducer: {
todos: todosReducer,
filters: filtersReducer,
},
})
請注意,這個 configureStore
呼叫會自動執行所有你會手動執行的常見設定工作
- 區塊 reducer 會自動傳遞給
combineReducers()
redux-thunk
中介軟體會自動新增- 開發模式中介軟體會新增以捕捉意外變異
- Redux DevTools 擴充功能會自動設定
- 中介軟體和 DevTools 增強器會組合在一起並新增到儲存體
同時,configureStore
提供選項讓使用者修改任何這些預設行為(例如關閉 thunk 並新增 sagas,或在製作中停用 DevTools),
從那裡開始,Redux Toolkit 包含其他 API 以執行常見的 Redux 任務
createAsyncThunk
:抽象出標準「在非同步請求之前/之後發送動作」模式createEntityAdapter
:針對正規化狀態的 CRUD 作業預先建置的 reducer 和 selectorcreateSelector
:標準 Reselect API 的重新導出,用於備忘式 selectorcreateListenerMiddleware
:一個副作用中介軟體,用於執行邏輯以回應已發送的動作
最後,RTK 套件還包含「RTK Query」,一個完整的資料擷取和快取解決方案,適用於 Redux 應用程式,作為一個獨立的選用 @reduxjs/toolkit/query
進入點。它讓您可以定義端點(REST、GraphQL 或任何非同步函式),並產生一個 reducer 和中介軟體,完全管理資料擷取、更新載入狀態和快取結果。它還會自動產生 React hooks,可以在元件中用於擷取資料,例如 const { data, isFetching} = useGetPokemonQuery('pikachu')
這些 API 各自都是完全選用的,並設計用於特定的使用案例,而且您可以挑選並選擇您實際在應用程式中使用的 API。不過,強烈建議使用所有這些 API 來協助處理這些任務。
請注意,Redux Toolkit 仍然是「Redux」!仍然只有一個儲存,其中包含用於更新的已發送動作物件,以及不可變地更新狀態的 reducer,加上撰寫非同步邏輯的 thunk、管理正規化狀態、使用 TypeScript 為您的程式碼輸入類型,以及使用 DevTools 的能力。您只需編寫更少的程式碼,就能獲得相同的結果!
為什麼我們希望您使用 Redux Toolkit
作為 Redux 維護人員,我們的意見是
我們希望所有 Redux 使用者使用 Redux Toolkit 撰寫他們的 Redux 程式碼,因為它簡化了您的程式碼並消除了許多常見的 Redux 錯誤和 bug!
早期 Redux 模式的「樣板」和複雜性從來都不是 Redux 的必要部分。這些模式只存在是因為
- 原始「Flux 架構」使用了一些相同的做法
- 早期 Redux 文件顯示了像動作類型常數之類的東西,以便按類型將程式碼分隔到不同的檔案中
- JavaScript 預設是一個可變語言,而撰寫不可變更新需要手動物件展開和陣列更新
- Redux 最初僅在幾週內建置完成,並刻意設計成僅包含幾個 API 原語
此外,Redux 社群已採用一些特定方法,增加了額外的樣板
- 強調使用
redux-saga
中介軟體作為撰寫副作用的常見方法 - 堅持手寫 Redux 動作物件的 TS 類型,並建立聯合類型以限制可在類型層級中發送的動作
多年來,我們已了解人們實際上如何使用 Redux。我們已了解社群如何撰寫數百個附加函式庫,以執行產生動作類型和建立函式、非同步邏輯和副作用,以及資料擷取等任務。我們也了解持續為我們的使用者帶來痛苦的問題,例如意外變異狀態、僅為了進行一個簡單的狀態更新而撰寫數十行程式碼,以及難以追蹤程式碼庫如何組合在一起。我們已協助數千名嘗試學習和使用 Redux 的使用者,並努力了解所有部分如何組合在一起,且對於他們必須撰寫的概念數量和額外程式碼量感到困惑。我們知道我們的使用者面臨哪些問題。
我們特別設計了 Redux Toolkit 來解決這些問題!
- Redux Toolkit 將儲存設定簡化為單一明確的函式呼叫,同時保留在需要時完全設定儲存選項的能力
- Redux Toolkit 消除了意外變異,這一直是 Redux 錯誤的頭號原因
- Redux Toolkit 消除了手寫任何動作建立函式或動作類型的需求
- Redux Toolkit 消除了撰寫手動且容易出錯的不可變更新邏輯的需求
- Redux Toolkit 讓您可以輕鬆地在一個檔案中撰寫 Redux 功能的程式碼,而不是將其分散在多個獨立檔案中
- Redux Toolkit 提供優異的 TS 支援,其 API 旨在為您提供優異的類型安全性,並將您必須在程式碼中定義的類型數量減至最少
- RTK Query 可以消除撰寫任何 thunk、reducer、動作建立函式或效應掛勾的需求,以管理資料擷取和追蹤載入狀態
因此
我們特別建議我們的使用者應該使用 Redux Toolkit(@reduxjs/toolkit
套件),並且不應該將舊版 redux
核心套件用於任何新的 Redux 程式碼!
即使對於現有的應用程式,我們建議至少將 createStore
切換為 configureStore
,因為開發模式中介軟體也能協助您在現有的程式碼庫中找出意外的變異和序列化錯誤。我們也鼓勵您將最常使用的 reducer (以及未來撰寫的任何 reducer) 切換為 createSlice
,如此一來,程式碼將會更簡潔且易於理解,而且安全性改善將為您節省時間和精力。
redux
核心套件仍然有效,但我們現在認為它已過時。它的所有 API 也從 @reduxjs/toolkit
重新匯出,而 configureStore
能執行 createStore
的所有功能,但預設行為和設定性更佳。
了解較低層級的概念很有用,如此一來,您就能更了解 Redux Toolkit 為您執行了哪些動作。這就是 「Redux Fundamentals」教學課程會說明 Redux 如何運作,且不使用抽象化的原因。但是,它將這些範例視為學習工具,並在最後說明 Redux Toolkit 如何簡化舊版的親手撰寫 Redux 程式碼。
如果您單獨使用 redux
核心套件,您的程式碼將會繼續運作。但是,我們強烈建議您切換到 @reduxjs/toolkit
,並更新您的程式碼以改用 Redux Toolkit API!
進一步資訊
請參閱這些文件頁面和部落格文章以取得更多詳細資訊