Reduxとは?
Reduxとはグローバルな状態を管理するために使用されるライブラリです。
コンポーネント全体で管理すべき、グローバルな状態がReduxで管理されます。
- ログイン状態
- ローディング状態
グローバルな値を1つのStoreで管理することで、propsのバケツリレーから解放されます。
この記事では、Reduxの基本から状態管理について解説します。
Reduxの原則
Reduxは3つの基本原則で説明されます。
- 単一ソースで全ての値を管理する
- 状態は読み取り専用である
- 状態の更新は純粋関数によって行われる
Reduxでの状態管理と更新の流れ
Reduxでの状態管理と、状態の更新は以下のデータフローによって行われます。
- 入力をトリガーとして、
ActionCreatorが、対応するイベントを示すActionを作成 - データを管理する
Storeへ、ActionがDispatch(送信)される -
ActionがReducer(状態を変更する純粋関数)に渡される -
Reducerによって生成された新しいStateが保存される - 新しい
StateをViewがレンダリングする
Reduxの導入 ~ 状態管理
Reduxを使用するために、まずはライブラリをインストールします。
1. ライブラリのインストール
yarnを使用している場合
yarn add redux react-redux
npmを使用している場合
npm i redux react-redux
2. Action の定義
次に、Actionを作成します。
対応するイベントを示すActionを作成を行うActionCreatorを定義します。
// stores/action.ts
export const CounterActions = {
INCREMENT: 'counter/increment',
DECREMENT: 'counter/decrement',
} as const;
type ValueOf<T> = T[keyof T];
export interface CounterAction {
type: ValueOf<typeof CounterActions>;
payload?: unknown;
error?: boolean;
meta?: string;
}
// ActionCreators
export const increment = (): CounterAction => ({
type: CounterActions.INCREMENT,
});
export const decrement = (): CounterAction => ({
type: CounterActions.DECREMENT,
});
Action
ReduxにおけるActionは、アプリケーションの状態を変更するための指示を含むオブジェクトです。
Actionはtypeというプロパティを持ち、このtypeプロパティがアクションの種類を示します。また、必要に応じてpayloadやerror、metaといった追加のプロパティを持つことができます。
FSA
FSA(Flux Standard Action)は、Actionの標準形式を定めた規格です。
FSAに従うアクションオブジェクトは、次のプロパティを持ちます。
-
type:アクションの種類を示す文字列 -
payload?:アクションが関与するデータ -
error?:アクションがエラーであることを示すブール値 -
meta?:アクションに関するメタデータ
Actionは基本的にはFSAに順守した形で作成するようにしましょう。
3. Reducerの定義
Actionの次は、状態を変更する純粋関数である Reducer の実装です。
Reducerは、prevStateとActionを受け取り、newStateを返す純粋関数です。
Actionに応じた状態の更新処理を実装します。
// stores/reducer.ts
import { Reducer } from 'redux';
import { CounterAction, CounterActions } from './action';
export interface CounterState {
count: number;
}
export const initialState: CounterState = { count: 1 };
export const counterReducer: Reducer<CounterState, CounterAction> = (
state = initialState,
action: CounterAction,
) => {
switch (action.type) {
case CounterActions.INCREMENT:
return {
...state,
count: state.count + 1,
};
case CounterActions.DECREMENT:
return {
...state,
count: state.count - 1,
};
// defaultの定義がないとエラーになるので注意
default:
return state;
}
};
4. ActionをDispatcherに渡す
useDispatchフックを使用して、StoreにActionを送信します。
送信されたActionは、Reducerによって処理され、状態が更新されます。
5. 状態の取得
useSelectorフックを使用して、Storeから状態を取得します。
セレクト関数は、Storeの状態全体を引数として受け取り、必要な状態を返します。
// Counter.tsx
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { decrement, increment } from './action';
import { CounterState } from './reducer';
const Counter: React.FC = () => {
const count = useSelector<CounterState, number>((state) => state.count);
const dispatch = useDispatch();
return (
<>
<div>count: {count}</div>
<div>
<button onClick={() => dispatch(increment())}>increment</button>
</div>
<div>
<button onClick={() => dispatch(decrement())}>decrement</button>
</div>
</>
);
};
export default Counter;
6. Reduxストアの作成とプロバイダーの設定
最後に、Reduxストアを作成し、Reactアプリケーションに提供するためのプロバイダを指定しましょう。
// src/Providers.tsx
import { FC, PropsWithChildren } from 'react';
import { HelmetProvider } from 'react-helmet-async';
import { Provider as ReduxProvider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { createStore } from 'redux';
import { counterReducer, initialState } from './reducer';
const store = createStore(counterReducer, initialState);
const Providers: FC<PropsWithChildren> = ({ children }) => (
<BrowserRouter>
<HelmetProvider>
<ReduxProvider store={store}>{children}</ReduxProvider>
</HelmetProvider>
</BrowserRouter>
);
export default Providers;
Redux 公式スタイルガイド
Reduxを利用するにあたり、公式からでているスタイルガイドも一読しておきましょう。
個人的に注意しておきたい点をピックアップしておきます。
Action
-
Action名は「ドメインモデル/イベント種別」のフォーマットで定義する -
ActionはFSA(Flux Standard Action)に準拠させる -
Actionは直に書かずAction Creatorを使って生成する
Design Pattern
- ロジックを書くときは
Redux Toolkitを使う - イミュータブルな状態の更新には
Immerを使う - ファイル構造には
Feature FolderまたはDucksパターンを適用する
まとめ
この記事では、Reduxの基本についてまとめてみました。
難しいイメージを持っていましたが、なんとかなった...
RTK(Redux Tool Kit)を使用した方が楽に実装できますが、プレーンなRedux実装についても理解しておくといい気がします。

