跳至主要內容

Redux Toolkit TypeScript 快速入門

你將會學到
  • 如何設定並使用 Redux Toolkit 和 React-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)匯出它們,並直接匯入到其他檔案中。

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

定義輸入的掛鉤

雖然可以將 RootStateAppDispatch 類型匯入到每個元件中,但最好為應用程式中的 useDispatchuseSelector 掛鉤建立輸入的版本。這很重要,原因有幾個

  • 對於 useSelector,它讓您省去每次輸入 (state: RootState) 的需要
  • 對於 useDispatch,預設的 Dispatch 類型並不知道 thunk。為了正確地傳送 thunk,您需要使用包含 thunk 中介軟體類型的商店中的特定自訂 AppDispatch 類型,並將其與 useDispatch 一起使用。新增一個預先輸入的 useDispatch 勾子可讓您不會忘記在需要的地方匯入 AppDispatch

由於這些是實際變數,而不是類型,因此將它們定義在一個獨立的檔案中(例如 app/hooks.ts)非常重要,而不是儲存設定檔。這讓您可以將它們匯入任何需要使用勾子的元件檔中,並避免潛在的環狀匯入相依性問題。

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 編譯器可以正確地處理這些類型。這對於撰寫選取器函式等使用案例可能是必要的。

features/counter/counterSlice.ts
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 中的標準勾子。

features/counter/Counter.tsx
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 一起使用的詳細資訊。