[React] Reduxを理解する！

### Reduxとは？
Reduxとはグローバルな状態を管理するために使用されるライブラリです。

コンポーネント全体で管理すべき、グローバルな状態がReduxで管理されます。
- ログイン状態
- ローディング状態

グローバルな値を1つのStoreで管理することで、propsのバケツリレーから解放されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2842674/dd7b9114-918d-e74f-b6e4-b3985489a4c4.png)


この記事では、Reduxの基本から状態管理について解説します。

---

### Reduxの原則
Reduxは3つの基本原則で説明されます。

- 単一ソースで全ての値を管理する
- 状態は読み取り専用である
- 状態の更新は純粋関数によって行われる

https://redux.js.org/understanding/thinking-in-redux/three-principles

---

### Reduxでの状態管理と更新の流れ

Reduxでの状態管理と、状態の更新は以下のデータフローによって行われます。

1. 入力をトリガーとして、`ActionCreator`が、対応するイベントを示す`Action`を作成
1. データを管理する`Store`へ、`Action`が`Dispatch`（送信）される
1. `Action`が`Reducer`（状態を変更する純粋関数）に渡される
1. `Reducer`によって生成された新しい`State`が保存される
1. 新しい`State`を`View`がレンダリングする

![92alq46m4gh0v1sprqgf46m3f38r.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2842674/7af1f902-7c91-74f1-d157-9324e87dbaad.gif)


---

### Reduxの導入 ~ 状態管理

Reduxを使用するために、まずはライブラリをインストールします。

#### 1. ライブラリのインストール

yarnを使用している場合
`yarn add redux react-redux`

npmを使用している場合
`npm i redux react-redux`

---

#### 2. Action の定義

次に、Actionを作成します。

対応するイベントを示す`Action`を作成を行う`ActionCreator`を定義します。

```typescript
// 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,
});
```

#### :memo: Action

`Redux`における`Action`は、アプリケーションの状態を変更するための指示を含むオブジェクトです。
`Action`は`type`というプロパティを持ち、この`type`プロパティがアクションの種類を示します。また、必要に応じて`payload`や`error`、`meta`といった追加のプロパティを持つことができます。

#### :memo: FSA

`FSA（Flux Standard Action）`は、`Action`の標準形式を定めた規格です。
FSAに従うアクションオブジェクトは、次のプロパティを持ちます。

- `type`：アクションの種類を示す文字列
- `payload?`：アクションが関与するデータ
- `error?`：アクションがエラーであることを示すブール値
- `meta?`：アクションに関するメタデータ

Actionは基本的にはFSAに順守した形で作成するようにしましょう。

---

#### 3. Reducerの定義

`Action`の次は、**状態を変更する純粋関数**である **`Reducer`** の実装です。

`Reducer`は、`prevState`と`Action`を受け取り、`newState`を返す純粋関数です。
`Action`に応じた状態の更新処理を実装します。

```typescript
// 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`の状態全体を引数として受け取り、必要な状態を返します。

```tsx
// 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アプリケーションに提供するためのプロバイダを指定しましょう。

```tsx
// 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を利用するにあたり、公式からでているスタイルガイドも一読しておきましょう。

https://redux.js.org/style-guide/

個人的に注意しておきたい点をピックアップしておきます。

#### Action
- `Action`名は「ドメインモデル／イベント種別」のフォーマットで定義する
- `Action`は`FSA`（Flux Standard Action）に準拠させる
- `Action`は直に書かず`Action Creator`を使って生成する

#### Design Pattern
- ロジックを書くときは`Redux Toolkit`を使う
- イミュータブルな状態の更新には`Immer`を使う
- ファイル構造には`Feature Folder`または`Ducks`パターンを適用する 

---

### まとめ
この記事では、Reduxの基本についてまとめてみました。
難しいイメージを持っていましたが、なんとかなった...

`RTK`（Redux Tool Kit）を使用した方が楽に実装できますが、プレーンな`Redux`実装についても理解しておくといい気がします。