TypeScriptでRedux Toolkitと型を連携!初心者向け完全ガイド
生徒
「TypeScriptを使ってReactの開発をしているのですが、Redux Toolkitというものに型をつけるのが難しいです。どうすればいいですか?」
先生
「Redux Toolkitは、データの管理を楽にする道具です。TypeScriptと連携させることで、データの形をあらかじめ決めておき、間違いをすぐに発見できるようになりますよ。」
生徒
「パソコン初心者なので、型と言われてもピンとこないのですが、具体的にどう設定するのでしょうか?」
先生
「安心してください。まずは基本的な設定から、図解を交えて一つずつ丁寧に解説していきますね!」
1. Redux Toolkitと型連携のメリットとは
プログラミングを始めたばかりの方にとって、Redux ToolkitとTypeScriptの組み合わせは少し難しく感じるかもしれません。しかし、この二つを組み合わせる最大の理由は、プログラムが動かなくなる原因となるミスを、書いている途中で見つけるためです。パソコンを触ったことがない方でもイメージしやすいように、料理で例えてみましょう。
プログラムの中で扱うデータは、料理の材料のようなものです。型というのは、その材料を入れる専用の容器です。肉を入れる容器に魚を入れようとしたら、エラーとして教えてくれるのがTypeScriptの役割です。Redux Toolkitは、それらの容器をまとめて管理する冷蔵庫のような役割を果たします。型を連携させることで、どの棚に何が入っているかを完璧に把握できるようになり、開発が劇的にスムーズになります。
特にReactやNext.jsといった最新のフロントエンド開発では、このデータの管理が非常に重要です。検索エンジンでもよく調べられるこのテーマをマスターすれば、プログラミングの基礎体力がぐんと向上します。
2. データの形を決めるStateの定義
まずは、アプリの中で使いたいデータの形を決めます。これをプログラミング用語でState(ステート)と呼びます。ステートとは、現在のアプリの状態、例えばユーザーの名前や、数字のカウントなど、変化する情報のことを指します。
TypeScriptでは、このステートがどんな項目を持っているかを事前に宣言する必要があります。これをinterface(インターフェース)という仕組みを使って行います。インターフェースは、データの設計図のようなものです。例えば、カウンターアプリを作る場合、数字を保存するための箱の名前と、それが数字型であることを定義します。
// カウンターのデータの形を定義する
interface CounterState {
value: number; // 数値を扱うための項目
}
// 最初の状態(初期値)を決める
const initialState: CounterState = {
value: 0,
};
このように、最初にデータのルールを決めておくことで、後から間違えて文字を入れてしまうといった失敗を防ぐことができます。
3. Slice(スライス)の作成と型の適用
次に、データの処理をまとめたSlice(スライス)を作成します。スライスとは、特定の機能ごとにデータを切り分けたものです。例えば、ユーザー情報に関するスライス、商品リストに関するスライスといった具合です。
Redux ToolkitのcreateSliceという機能を使うと、データの初期値と、そのデータをどう変更するかというルールを一度に書くことができます。ここで先ほど作成したインターフェースを使います。これにより、このスライスの中では常に正しい形のデータが扱われることが保証されます。
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
export const counterSlice = createSlice({
name: 'counter',
initialState, // 先ほど決めた初期値を使う
reducers: {
// 数字を増やす処理
increment: (state) => {
state.value += 1;
},
// 指定した分だけ数字を増やす処理(PayloadActionで型を指定)
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
});
ここで出てくるPayloadActionというのは、処理に渡される追加情報に型をつけるためのものです。例えば、5増やすという指示の5という数字が、正しく数値として渡されているかをチェックしてくれます。
4. Store(ストア)の設定とRootStateの書き出し
作成したスライスを、アプリ全体で使えるようにまとめる場所をStore(ストア)と呼びます。ストアは、アプリ内のすべてのデータを一括管理する大きな倉庫のようなものです。
TypeScriptを使う場合、このストアが持っているデータ全体の形を自動的に取得して、型として定義することが推奨されます。これを行うことで、アプリのどこからでも安全にデータを参照できるようになります。ReturnTypeという特殊な書き方を使いますが、これはストアの状態から型を逆引きする便利な魔法のようなものだと考えてください。
import { configureStore } from '@reduxjs/toolkit';
export const store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
});
// ストア全体の型を定義する(RootState)
export type RootState = ReturnType<typeof store.getState>;
// データを送るための関数の型を定義する(AppDispatch)
export type AppDispatch = typeof store.dispatch;
この設定を行っておくことで、後ほどコンポーネントの中でデータを取り出す際に、何の設定もなしに自動補完が効くようになります。
5. Hooks(フック)の型付けと共通化
Reactの中でReduxのデータを操作するには、useSelectorとuseDispatchという二つのツールを使います。しかし、これらをそのまま使うたびに型を指定するのは非常に手間がかかります。そこで、あらかじめ型情報を持たせた自分専用のフックを作成するのが一般的です。
これをプログラミングではカスタムフックと呼びます。一度設定してしまえば、あとは呼び出すだけでTypeScriptの恩恵を受けることができます。パソコン初心者の方は、自分専用の便利なショートカットボタンを作るようなイメージで捉えてください。
import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux';
import type { RootState, AppDispatch } from './store';
// 型がついた状態のdispatch(指示出し役)を作る
export const useAppDispatch = () => useDispatch<AppDispatch>();
// 型がついた状態のselector(データ取得役)を作る
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
このファイルを用意しておけば、実際の画面を作るファイルでは、わざわざ複雑な型を書く必要がなくなります。非常にスマートで効率的な開発スタイルです。
6. Reactコンポーネントでの実践的な使い方
それでは、準備した型付きのツールを実際の画面(コンポーネント)で使ってみましょう。Reactの部品の中で、先ほど作ったuseAppSelectorとuseAppDispatchを呼び出します。
データを表示したいときはセパレーターを使い、データを変更したいときはディスパッチを使って命令を送ります。このとき、もし間違った名前のデータにアクセスしようとしたり、数字を入れるべき場所に文字を入れようとしたりすると、エディタがすぐに赤線を引いて教えてくれます。これがTypeScriptとReduxを連携させる最大のメリットです。
import React from 'react';
import { useAppSelector, useAppDispatch } from './hooks';
import { counterSlice } from './counterSlice';
export const CounterComponent = () => {
const count = useAppSelector((state) => state.counter.value);
const dispatch = useAppDispatch();
const { increment } = counterSlice.actions;
return (
<div>
<p>現在の数字: {count}</p>
<button onClick={() => dispatch(increment())}>増やす</button>
</div>
);
};
プログラムを実行すると、ボタンを押すたびに画面の数字が1ずつ増えていくはずです。裏側ではTypeScriptがしっかりとデータの流れを見守ってくれています。
実行結果:
画面に「現在の数字: 0」と表示される。
ボタンを押すと「現在の数字: 1」に変化する。
7. エラーを防ぐ!非同期処理と型の連携
実際のアプリ開発では、インターネットを通じて外部からデータを取得することがよくあります。これを非同期処理と呼びます。Redux Toolkitには、このような処理を簡単に行うためのcreateAsyncThunkという機能があります。
外部からどんなデータが返ってくるかを型で定義しておくことで、通信が成功したときや失敗したときの処理も安全に書くことができます。例えば、ユーザー一覧を取得する場合、配列の中にどのようなユーザー情報が入っているかを指定します。これにより、取得したデータを使うときに「名前を表示しようとしたらデータが空だった」といった不測の事態を避けることができます。
初心者のうちは、通信処理は難しく感じるかもしれませんが、型をしっかり定義していれば、TypeScriptがガイド役となって次に書くべきコードを提案してくれます。恐れずに挑戦してみましょう。
8. TypeScriptとReduxを学ぶための心構え
ここまで、TypeScriptとRedux Toolkitを連携させる方法を解説してきました。パソコンを触り始めたばかりの方にとって、専門用語の多さに驚いたかもしれません。しかし、これらはすべて、あなたが書いたプログラムがバグ、つまり不具合を起こさないように守ってくれるための仕組みです。
一度にすべてを暗記する必要はありません。まずはテンプレートとして決まった形を書き写すことから始めてみてください。何度も繰り返して使っているうちに、「ここで型を指定したから、あっちでミスに気づけたんだ!」という体験が必ず訪れます。その積み重ねが、エンジニアとしての確かな実力に繋がっていきます。
ReactやNext.jsの世界は日々進化していますが、このデータの管理と型の重要性は変わりません。基本を大切にして、一歩ずつ進んでいきましょう。分からない用語があれば、その都度調べて知識を深めていくのが上達の近道です。