はじめに
私は、働く人が働きやすく、企業は働きやすいから人が育ち、より良いサービスを提供できるから多くの人が喜べる。と考えております。
このサイクルで大きな一助になれるのは、エンジニアの力。企業発展や働く人がより輝けるWEBサービスを提供できるエンジニアを目指し、1年ほど前から独学を始め、現在はAPPRENTICE3期生として学ばせていただいております。
APPRENTICEについて
この記事の目的
初学者の方が、まずはReduxを利用して恩恵を受けられる状態にすることを目的としております。
APPRENTICEの課題でWEBアプリケーションの個人開発があり、アプリケーション全体で状態管理が必要となり、Reduxを利用しました。ドキュメントやReduxに関する記事は非常に多くありますが、初学者からすると聞きなれない見慣れない言葉が多くとっつきにくかったので、一助になればと思いこちらにまとめます。
使用環境
- MacBook Pro : M1 チップ / 16 GB
- Next.js : 14.2.2
- React : 18
- Redux : 9.1.2
- Redux Toolkit : 2.2.3
Reduxの利用方法
今回参考にさせていただいたのは、Redux Toolkitの主要な開発者のMark Eriksonさんの説明されていたYouTubeから引用しています。
Let’s Learn Modern Redux! (with Mark Erikson) — Learn With Jason
この動画の内容を基に、サンプルとしてよく作られるカウンターアプリケーションを作成していきます。
Next.js(React)のプロジェクト作成、Redux、ReduxToolkitのインストール方法はドキュメントの記述から開発環境に合わせて行ってください。
1. sliceファイルの作成
目的:コンポーネントをまたいで利用したい値と値をどのように更新するか記していきます。
ファイル作成のディレクトリはどこでも問題ないので、私はRedux関わる処理をすべてプロジェクトのルートディレクトリにstoreというディレクトリを作成しました。
動画26:00~の内容に該当します。
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
// 状態管理したい値の型定義を行う
interface CounterState {
value: number;
}
// 初期状態の値を定義
const initialState: CounterState = {
value: 5
}
// sliceを定義する
const counterSlice = createSlice({
// nameはRedux DevToolsで処理内容を追いやすくするためのものです
// 動画42:30~こちらに記述したnameについて説明されています。
name: "counter",
// 定義した値の初期値
initialState,
// 値を同期処理においてどのように更新するかをreducersの中にまとめて記述します。
// 以下ではincrementとamountAddという二つの処理を記述しています。
// こちらの更新用メソッドの引数にはメソッドを呼び出した時にstateの値とコンポーネントでメソッドを呼び出した時に渡される引数がaction.payloadに格納されています
reducers: {
increment(state) {
state.value++;
},
amountAdd(state, action: PayloadAction<number>){
state.value += action.payload;
},
},
})
// reducersに記述したメソッドをexportして、利用したいコンポーネントで利用できるようにします。
export const { increment, amountAdd } = counterSlice.actions;
// reduxはsliceで定義した内容をstoreで一元管理するので、作成したsliceをexportします。
export default counterSlice.reducer;
2. storeファイルの作成
目的:sliceで定義した値と更新処理の内容をstoreに登録することで、アプリケーション全体でそれらを利用できるようにしてくれます。
動画35:40~の内容に該当します。
// app > store > store.ts
// store作成するための関数をインポート
import { configureStore } from "@reduxjs/toolkit";
// storeに登録して、グローバルで利用したいsliceを読み込みます。
// sliceでdefault exportしているので、reducerとわかりやすくするために名前を変更しているだけです。
import counterReducer from "./counterSlice";
export const store = configureStore({
reducer:{
// このプロパティ名をコンポーネントで値を呼び出すときに利用します。
counter: counterReducer,
}
})
// Redux Toolkitがコンポーネントで利用する際に必要となる値に対する型と値の更新に利用するメソッドに関する型を定義してくれる。これをexportしてコンポーネントで利用します。
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>
3. hooksファイルの作成
※ Java Scriptでは不要なファイルです。
目的:Reduxの状態管理している値や更新用メソッドを利用する際に、本来であれば毎回storeでexportした型を記述する必要があります。
しかしその手間をはぶくためにタイプセーフされた状態で、記述を短縮するためにhooks.tsを作成します。
動画42:50~の内容に該当します。
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { RootState, AppDispatch } from "./store";
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
4. Reduxで定義した値、更新用メソッドを利用するコンポーネントの親コンポーネントにてProviderでラップする
目的:Reduxで定義した値、更新用メソッドを利用するため
動画40:30に該当する内容です。
"use client";
import { Provider } from "react-redux";
import Calc from "./calc/Calc";
import { store } from "./store/store";
import Counter from "./counter/Counter";
export default function Home() {
return (
// このproviderにstoreを渡すことでReduxの値、更新用メソッドを利用することができます。
<Provider store={store}>
<main>
<Counter />
</main>
</Provider>
);
}
5. コンポーネントで値と更新用関数を呼び出して利用する
import { useAppDispatch, useAppSelector } from "../store/hooks"
import { amountAdd, increment } from "../store/counterSlice"
export default function Counter() {
// hooksで定義したuseAppSelectorを呼び出し、このhooksの引数にredux全体の値が格納されているため、storeのreducer名を指定することで、特定のsliceの値を取得できる
const count = useAppSelector((state) => state.counter.value);
// dispatchを受け取って、呼び出すことで処理を実行することが可能となる
const dispatch = useAppDispatch();
const clickHandler = () => {
// dispatch(increment())
// 呼び出した更新用メソッドに引数を渡すと、sliceではaction.payloadとして受け取ることができる。複数の値を渡す必要があるならオブジェクトにして渡す
dispatch(amountAdd(3));
}
return (
<>
<button onClick={clickHandler}>+</button>
<h1>count is: {count}</h1>
</>
)
}
まとめ
こちらの記事ではパフォーマンスや本質的なことはなにもお伝えできていないかと思いますが、初学者としてハードルが高そうに思えるReduxをまずは触れられることを目的としております。
同期的な更新処理してしておりませんが、実際にまずは利用してみることで理解やThunkなどの利用にもつなげられるかと思いますので、なにかしらの一助になれば幸いです。
参考動画
YouTube
Let’s Learn Modern Redux! (with Mark Erikson) — Learn With Jason
https://www.youtube.com/watch?v=9zySeP5vH9c&t=3528s