Redux Toolkit TypeScript 快速入門
- 如何使用 TypeScript 設定並使用 Redux Toolkit 和 React-Redux
- 了解 React Hooks
- 了解 Redux 術語和概念
- 了解 TypeScript 語法和概念
簡介
歡迎使用 Redux Toolkit TypeScript 快速入門教學!本教學將簡要說明如何將 TypeScript 與 Redux Toolkit 和 React-Redux 搭配使用。
此頁面僅著重於如何設定 TypeScript 層面。若要了解 Redux 的內容、運作方式,以及 Redux Toolkit 的完整使用範例,請參閱「教學索引」頁面中連結的教學。
Redux Toolkit 已使用 TypeScript 編寫,因此其 TS 型別定義已內建。
React Redux 自版本 8 起也已使用 TypeScript 編寫,並包含其自己的型別定義。
適用於 Create-React-App 的 Redux+TS 範本附有已設定好這些模式的實際範例。
專案設定
定義根狀態和傳送類型
Redux Toolkit 的 configureStore
API 不需要任何額外的型別。不過,您會想要擷取 RootState
型別和 Dispatch
型別,以便在需要時參照它們。從儲存體本身推論這些型別表示,當您新增更多狀態區段或修改中間件設定時,它們會正確更新。
由於這些是型別,因此可以安全地從儲存體設定檔(例如 app/store.ts
)直接匯出它們,並直接匯入其他檔案。
import { configureStore } from '@reduxjs/toolkit'
// ...
export const store = configureStore({
reducer: {
posts: postsReducer,
comments: commentsReducer,
users: usersReducer
}
})
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
定義型別掛鉤
雖然可以將 RootState
和 AppDispatch
型別匯入每個元件,但最好為應用程式中的 useDispatch
和 useSelector
掛鉤建立型別版本。這有幾個原因很重要
- 對於
useSelector
,它讓您不必每次都輸入(state: RootState)
- 對於
useDispatch
,預設的Dispatch
型別不知道 thunk。為了正確傳送 thunk,您需要使用儲存體中包含 thunk 中間件型別的特定自訂AppDispatch
型別,並將其與useDispatch
搭配使用。新增預先型別化的useDispatch
掛鉤可讓您不會忘記在需要的地方匯入AppDispatch
。
由於這些是實際變數,而非型別,因此務必在 app/hooks.ts
等獨立檔案中定義它們,而不是儲存體設定檔。這讓您可以將它們匯入需要使用掛鉤的任何元件檔案,並避免潛在的循環匯入依賴關係問題。
import { useDispatch, useSelector } from 'react-redux'
import type { AppDispatch, RootState } from './store'
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
export const useAppSelector = useSelector.withTypes<RootState>()
應用程式使用
定義區塊狀態和動作類型
每個區塊檔案都應定義其初始狀態值類型,以便 createSlice
能正確推斷每個案例簡化器的 state
類型。
所有產生的動作都應使用 Redux Toolkit 中的 PayloadAction<T>
類型定義,其採用 action.payload
欄位的類型作為其泛型引數。
你可以從這裡的儲存檔案安全匯入 RootState
類型。這是循環匯入,但 TypeScript 編譯器可以正確處理類型。這對於撰寫選擇器函式等使用案例可能是必要的。
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import type { RootState } from '../../app/store'
// Define a type for the slice state
export interface CounterState {
value: number
}
// Define the initial state using that type
const initialState: CounterState = {
value: 0
}
export const counterSlice = createSlice({
name: 'counter',
// `createSlice` will infer the state type from the `initialState` argument
initialState,
reducers: {
increment: state => {
state.value += 1
},
decrement: state => {
state.value -= 1
},
// Use the PayloadAction type to declare the contents of `action.payload`
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload
}
}
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
// Other code such as selectors can use the imported `RootState` type
export const selectCount = (state: RootState) => state.counter.value
export default counterSlice.reducer
產生的動作建立器將正確設定為接受 payload
引數,其根據你為簡化器提供的 PayloadAction<T>
類型。例如,incrementByAmount
需要 number
作為其引數。
在某些情況下,TypeScript 可能會不必要地收緊初始狀態的類型。如果發生這種情況,你可以使用 as
轉換初始狀態,而不是宣告變數的類型來解決此問題
// Workaround: cast state instead of declaring variable type
const initialState = {
value: 0
} as CounterState
在元件中使用型別化掛勾
在元件檔案中,匯入預先設定型別的掛勾,而不是 React-Redux 中的標準掛勾。
import React from 'react'
import { useAppSelector, useAppDispatch } from 'app/hooks'
import { decrement, increment } from './counterSlice'
export function Counter() {
// The `state` arg is correctly typed as `RootState` already
const count = useAppSelector(state => state.counter.value)
const dispatch = useAppDispatch()
// omit rendering logic
}
完整的計數器應用程式範例
以下是完整的 TS 計數器應用程式,作為執行中的 CodeSandbox
接下來是什麼?
我們建議瀏覽完整的「Redux Essentials」教學課程,其中涵蓋 Redux Toolkit 中包含的所有關鍵部分、它們解決的問題以及如何使用它們來建置真實世界的應用程式。
你可能還想閱讀「Redux Fundamentals」教學課程,這將讓你全面了解 Redux 的運作方式、Redux Toolkit 的功能以及如何正確使用它。
最後,請參閱「與 TypeScript 搭配使用」頁面,以取得有關如何將 Redux Toolkit 的 API 與 TypeScript 搭配使用的詳細資訊。