本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
Redux Toolkit TypeScript 快速入门
- 如何配置并使用 Redux Toolkit 和 React-Redux 与 TypeScript
- 掌握 React Hooks
- 理解 Redux 术语和核心概念
- 熟悉 TypeScript 语法和概念
简介
欢迎来到 Redux Toolkit TypeScript 快速入门教程!本教程将简要介绍如何在 Redux Toolkit 和 React-Redux 中使用 TypeScript。
本文重点介绍 TypeScript 相关配置。如需了解 Redux 的工作原理及完整使用示例,请参阅"教程索引"页面的相关教程。
Redux Toolkit 本身使用 TypeScript 编写,因此内置了 TS 类型定义。
React Redux 从第 8 版开始也采用 TypeScript 编写,同样包含自身的类型定义。
Create-React-App 的 Redux+TS 模板已包含这些模式的配置示例。
项目配置
定义根状态与派发类型
Redux Toolkit 的 configureStore API 通常无需额外类型声明。但建议提取 RootState 和 Dispatch 类型以便全局引用。从 store 本身推断这些类型可确保在添加状态切片或修改中间件时自动更新类型定义。
由于这些是类型声明,可直接从 app/store.ts 等 store 配置文件导出,并直接导入到其他文件中使用。
import { configureStore } from '@reduxjs/toolkit'
// ...
export const store = configureStore({
reducer: {
posts: postsReducer,
comments: commentsReducer,
users: usersReducer
}
})
// Infer the `RootState`, `AppDispatch`, and `AppStore` 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
export type AppStore = typeof store
定义类型化钩子
虽然可以独立导入 RootState 和 AppDispatch 类型到各组件,但更推荐为应用创建类型化的 useDispatch 和 useSelector 钩子,原因如下:
-
对于
useSelector,可避免每次手动指定(state: RootState) -
对于
useDispatch,默认的Dispatch类型无法识别 thunk。要正确派发 thunk,需使用 store 中包含了 thunk 中间件类型的特定定制AppDispatch类型,并将其与useDispatch一起使用。预定义类型化的useDispatch钩子可避免在需要时忘记导入AppDispatch
由于这些是实际变量而非类型声明,应在独立文件(如 app/hooks.ts)中定义,而非 store 配置文件。这样可避免循环依赖问题,并支持在任意组件中导入使用。
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 能正确推断每个 case reducer 中的 state 类型。
所有生成的动作应使用 Redux Toolkit 的 PayloadAction<T> 类型定义,其泛型参数对应 action.payload 字段的类型。
可安全地从 store 文件导入 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
生成的动作创建器将基于 reducer 中定义的 PayloadAction<T> 类型正确接收 payload 参数。例如 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
}
完整计数器应用示例
以下是在 CodeSandbox 上运行的完整 TypeScript 计数器应用示例:
下一步是什么?
我们建议学习 完整的 "Redux 精要" 教程,其中涵盖了 Redux Toolkit 的所有关键部分、解决的问题以及如何用于构建实际应用。
您也可以阅读 “Redux 基础”教程,它将帮助您全面理解 Redux 的工作原理、Redux Toolkit 的作用以及如何正确使用。
最后,请查阅 TypeScript 使用指南 获取关于如何在 TypeScript 中使用 Redux Toolkit API 的详细说明。