はじめに
Reactを勉強してきて、reduxを使うとstate管理が簡単になると聞ききました。しかし、理解するのが難しいと言われたので後回しにしてましたが、今回reduxを使ってstate管理をやっています。正確にはreduxを簡単に設計して利用できるredux toolkitを使ってやってみました。
独学で勉強している初心者であるため、間違っている内容が多い可能性があるのでご指摘いただくと幸いです。
開発状況
- バックエンド: Node.js + express + typescript
- フロントエンド: React + typescript + tailwind
redux toolkitを使ってGoal作成の実装
今回作っているものとしては、始める際に何から手を出せばわかない状態の時、目標(Goal)を作成し、それに対する詳細な計画(Plan)を立て、もっと詳細なタスク(Task)を立てて管理できるものになります。その中のGoal作成の部分をredux toolkitを利用して実装しました。
実装
stateの初期状態を定義
const initialState: GoalState = {
goals: [],
selectedGoal: null,
loading: false,
error: null,
};
このinitialState
は、Goalに関連するすべての状態を管理するための初期値を定義しています。今回のコードは以下のように状態を管理します。
- goals: ユーザーが作成した目標を配列形式で管理します。
- selectedGoal: 現在選択した目標を保持します(後で目標(goal)を選択し、目標の細分化する計画(plan)を作成する予定です)。
- loading: API通信中にtrueに切り替わり、通信が完了するとfalseに戻ります。これによりローディングを表示できます。
- error: エラーが発生した場合に、そのメッセージを表示するために使います。
Goal作成の非同期処理
export const createGoal = createAsyncThunk<
IGoal, // ReturnedType: サーバーから返される完全なデータ型
GoalFormInputs,
// ThunkArg: サーバーに送信するデータ型
{ rejectValue: ErrorResponse } // ThunkApiConfig: エラーレスポンス型
>("goal/create", async (goalData, { rejectWithValue }) => {
try {
const response = await axiosInstance.post(`api/goal/create`, goalData);
return response.data.newGoal;
} catch (error) {
if (axios.isAxiosError(error)) {
return rejectWithValue(error.response?.data as ErrorResponse);
} else {
return rejectWithValue({ message: "予期しないエラーが発生しました。" });
}
}
});
-
createAsyncThunk
- 非同期処理を簡潔に定義できるRedux Toolkitの機能です。
- 第一引数でアクション名、第二引数で非同期関数を指定します。
-
APIリクエスト
- axiosInstance.postを使い、api/goal/createエンドポイントにデータを送信します。
-
axiosInstance
とはAxiosのインスタンスを作成し、リクエストを簡略化するための仕組みになります。 -
axiosInstance
については今後整理したいと思います。
Redux Sliceの作成
const goalSlice = createSlice({
name: "goal", // Sliceの名前(
initialState, // 初期状態
reducers: {}, // 同期処理用のReducer
extraReducers: (builder) => {
builder
.addCase(createGoal.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(createGoal.fulfilled, (state, action) => {
state.loading = false;
state.goals.push(action.payload); Goalを追加
})
.addCase(createGoal.rejected, (state, action) => {
state.loading = false;
state.error = (action.payload as ErrorResponse).message;
});
},
});
-
createSlice
の役割は状態とReducerを一括管理します。 -
extraReducersの構成
- createAsyncThunkの状態(pending, fulfilled, rejected)に応じて、stateを更新します。
-
pending:
- 非同期処理が開始された際に、loadingをtrueにしてローディング状態を表現。
- エラーをリセットして、以前のエラーを消去。
-
fulfilled:
- 非同期処理が成功した場合、loadingをfalseに戻します。
- サーバーから返ってきたデータをgoalsに追加します。
-
rejected:
- 非同期処理が失敗した場合、loadingをfalseに戻します。
- サーバーから返されたエラーメッセージをerrorに格納します。
まとめ
今回はじめてRedux toolkitを学習して適用してみました。時間がかかりましたが、これからreduxを使って状態管理ができるように頑張ります。
次回は、Goalの選択や詳細表示の実装、さらには計画(Plan)やタスク(Task)の管理機能を実装したいと考えています。