跳至主要内容

React Redux TypeScript 快速入门

您将学到什么
  • 如何使用 TypeScript 设置和使用 Redux Toolkit 和 React Redux
先决条件

简介

欢迎使用 React Redux TypeScript 快速入门教程!本教程将简要介绍如何将 TypeScript 与 Redux Toolkit 和 React-Redux 一起使用

本页重点介绍如何设置 TypeScript 方面。有关 Redux 的解释、工作原理以及如何使用 Redux 的完整示例,请参阅 Redux 核心文档教程

React Redux 从版本 8 开始也使用 TypeScript 编写,并且还包含自己的类型定义。

用于 Create-React-App 的 Redux+TS 模板 附带已配置的这些模式的工作示例。

信息

最近更新的 @types/react@18 主要版本已更改组件定义以删除默认情况下将 children 作为道具。如果您在项目中有多个 @types/react 副本,这会导致错误。要解决此问题,请告诉您的包管理器将 @types/react 解析为单个版本。详情

https://github.com/facebook/react/issues/24304#issuecomment-1094565891

项目设置

定义根状态和调度类型

Redux Toolkit 的 configureStore API 不需要任何额外的类型。但是,您需要提取 RootState 类型和 Dispatch 类型,以便可以根据需要引用它们。从存储本身推断这些类型意味着它们会在您添加更多状态切片或修改中间件设置时正确更新。

由于这些是类型,因此可以安全地从您的商店设置文件(例如 app/store.ts)中直接导出它们,并直接导入到其他文件中。

app/store.ts
import { configureStore } from '@reduxjs/toolkit'
// ...

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,您需要使用来自存储的特定自定义 AppDispatch 类型,该类型包含 thunk 中间件类型,并将其与 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 可以正确推断每个情况 reducer 中 state 的类型。

所有生成的 action 应该使用 Redux Toolkit 中的 PayloadAction<T> 类型定义,该类型将 action.payload 字段的类型作为其泛型参数。

您可以安全地从商店文件中导入 RootState 类型。这是一个循环导入,但 TypeScript 编译器可以正确地处理类型。这对于编写选择器函数等用例可能很有必要。

features/counter/counterSlice.ts
import { createSlice, 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

生成的 action 创建者将被正确地类型化,以根据您为 reducer 提供的 PayloadAction<T> 类型接受 payload 参数。例如,incrementByAmount 需要一个 number 作为其参数。

在某些情况下,TypeScript 可能会不必要地收紧初始状态的类型。如果发生这种情况,您可以使用 as 对初始状态进行强制转换,而不是声明变量的类型,以此来解决它。

// Workaround: cast state instead of declaring variable type
const initialState = {
value: 0,
} 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 一起使用的更多详细信息。