Redux Toolkit TypeScript 快速入門
- 如何設定並使用 Redux Toolkit 和 React-Redux 搭配 TypeScript
- 了解 React Hooks
- 了解 Redux 術語和概念
- 了解 TypeScript 語法和概念
簡介
歡迎來到 Redux Toolkit TypeScript 快速入門教學!本教學將簡要說明如何將 TypeScript 與 Redux Toolkit 搭配使用。
此頁面專注於如何設定 TypeScript 面向。如需 Redux 是什麼、如何運作以及如何使用 Redux Toolkit 的完整範例的說明,請參閱「教學總覽」頁面中連結的教學。
Redux Toolkit 已以 TypeScript 編寫,因此其 TS 類型定義已內建。
React Redux 在 NPM 上的 @types/react-redux
型別定義套件 中有其類型定義。除了輸入函式庫函式外,這些類型還會匯出一些輔助程式,以簡化在 Redux 儲存和 React 元件之間撰寫類型安全介面的工作。
自 React Redux v7.2.3 起,react-redux
套件會相依於 @types/react-redux
,因此類型定義會隨函式庫自動安裝。否則,您需要手動安裝它們(通常為 npm install @types/react-redux
)。
Create-React-App 的 Redux+TS 範本 附帶已設定這些模式的實際範例。
專案設定
定義根狀態和派送類型
使用 configureStore 不需要任何其他輸入。但是,您會想要擷取 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 { RootState, AppDispatch } 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 } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
import type { RootState } from '../../app/store'
// Define a type for the slice state
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
產生的動作建立器將正確地輸入,以接受基於您為簡化器提供的 PayloadAction<T>
類型的 payload
引數。例如,incrementByAmount
需要一個 number
作為其引數。
在某些情況下,TypeScript 可能會不必要地收緊初始狀態的類型。如果發生這種情況,您可以使用 as
轉換初始狀態來解決這個問題,而不是宣告變數的類型
// Workaround: cast state instead of declaring variable type
const initialState = {
value: 0,
} satisfies CounterState as CounterState
在元件中使用輸入勾子
在元件檔中,匯入預先輸入的勾子,而不是 React-Redux 中的標準勾子。
import React, { useState } 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
}
下一步是什麼?
請參閱 「使用 TypeScript」頁面,以取得有關如何將 Redux Toolkit 的 API 與 TypeScript 一起使用的詳細資訊。