本文へスキップ

Redux Toolkit TypeScript クイックスタート

学習内容
  • Redux Toolkit と React-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などのストア設定ファイルから直接エクスポートし、他のファイルに直接インポートしても安全です。

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 { 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 コンパイラは型の処理を正しく行うことができます。これは、セレクター関数を記述するなど、ユースケースに必要な場合があります。

features/counter/counterSlice.ts
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

生成されたアクションクリエーターは、リデューサーに指定した 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 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 として、完全な TS カウンターアプリケーションを次に示します。

次のステップ

完全な「Redux Essentials」チュートリアル を参照することをお勧めします。これには、Redux Toolkit に含まれるすべての主要な要素、それらが解決する問題、およびそれらを使用して現実世界のアプリケーションを構築する方法が説明されています。

「Redux 基礎」チュートリアル も読むことをお勧めします。これにより、Redux の動作方法、Redux Toolkit の機能、および Redux Toolkit の正しい使用方法を完全に理解できます。

最後に、「TypeScript を使用した使用方法」ページ を参照して、TypeScript で Redux Toolkit の API を使用する詳細を確認してください。