無理してReduxを使わずに、Custom Hookで楽に状態を管理する 

## はじめに

Reduxを使わなくても、ReactでSPAをつくることは可能です。

画面の初期化時に一度だけ叩くapiをreduxで管理したことはありませんか？
apiを一度叩いて画面に表示するだけなのに、reducer/action/actionCreatorを作ってconnectをしてませんか？
Reduxを無理して使っていませんか？

自分が参画したWebアプリケーション開発では、Reactが採用される場合、常にReduxもセットになっていました。

Reduxは要件によってはオーバーエンジニアリングかもなーと感じることが多いので、改めてどんな時にReduxが有効かのか自分なりに考えてみました。

## SPAの設計で大事なこと

自分がフロントエンドの設計で大事だと思うのは、「ViewとStateの分離」です。複雑なアプリケーションでこれが出来ていないと可動性が低く変更しづらいです。

Viewはコンポーネントと呼ばれたりしますが見た目の部分です。
Stateはコンポーネントの状態です。本記事では、状態を変化させるロジックも含めStateと呼びます。

個人的には以下を実現することが重要です。

- ViewとStateが密結合にならず、コードの可読性を保てる。

- ユーザーが複雑な操作をしても、アプリケーションの状態が予測可能になっている。

## どんな時にReduxが有効か

Reduxが実現しているFluxアーキテクチャは、状態をStoreに、ロジックをAction/ActionCreatorにすることで、アプリケーションの状態を予測可能にしています。

Reduxを使うことで「ViewとStateの分離」は達成出来ます。
ですが、Reduxを使わなくても「ViewとStateの分離」は達成出来ます。
Redux辛いみたいな話はわざわざReduxで解決しなくても良い問題をReduxの冗長な処理を書いて解決しているからだと思います。

では、Reduxを使わないと辛い時はどんな時でしょうか？
自分は以下だと考えます。

- コンポーネントの階層が深い
- 画面で発火するイベントが多い

### componentの階層が深い

propsのバケツリレーと呼ばれる問題です。`prop drilling`の方が有名でしょうか。

![ey25z0hvmy31xiiqqwgq.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192435/2f6728aa-d705-3201-df2b-c4592b55c45a.png)
[出典：Learn React Context in 5 Minutes - A Beginner's Tutorial](https://techracho.bpsinc.jp/hachi8833/2018_03_13/53183)

Reactではコンポーネントにデータを渡す時にpropsを使います。
コンポーネントの階層が深いと、親コンポーネントから子コンポーネント、さらに孫コンポーネントと渡したいコンポーネントまで延々とpropsをバケツリレーのように渡していく必要があります。
このバケツリレーを省略するためにReduxを使うのは有用です。
自分は3回を超えたらRedux導入を考えます。

### 画面で発火するイベントが多い

1つの画面でユーザーがやれることが多い画面はReduxが有効だと判断します。
例えば、SlackやGoogle Carenderは1つの画面で様々なイベントを発火でき、リアルタイムにUIを変更しつつapiリクエストを飛ばします。

様々なイベントを高速でハンドリングする場合は、アプリケーションの状態が予測しづらくなるのでReduxを使います。
Reduxはdevtoolが強力でstoreに対する変更を全て記録してくれるのでデバッグもしやすいです。

## Custom Hookで「ViewとStateの分離」を実現する

一覧画面等で、画面初期化時に一度だけfetchしたデータを表示するのにReduxを使うのはオーバーエンジニアリングです。

自分はReactのCustom Hookを使います。

以下はユーザー情報を表示するだけの架空の画面の例です。

Custom HookにState（状態とロジック）を切り出します。

Custom HookはUtil的な使い方だけでなく、ドメイン固有の状態とロジックを管理するのにも有効です。

```useUser.ts
export const useUser = (): [
  User | undefined,
  () => void
] => {
  const [user, setUser] = React.useState<User | undefined>(undefined);
  const [errorMessage, setErrorMessage] = React.useState<string | undefined>(undefined);

  const fetchUser = async (): Promise<void> => {
    try {
      const res = await axios.get<User>('/api/user');
      setUser(res.data);
    } catch (e) {
      setError(e.response.date.message)
    }
  };

  return [
    user,
    errorMessage,
    fetchUser,
  ];
};
```

stateとstateを操作するロジックを`useUser`というCustom Hookに分離しています。
Reduxのreducer/action/actionCreatorをまとめたduckに近いですが、少ない記述量で「ViewとStateの分離」が出来ています。

```container.ts
import { useUser } from 'useUser';

export const UserContainer: React.FC = () => {
  const [user, errorMessage, fetchUser] = useUser();
  React.useEffect(() => {
    fetchUser();
  }, [fetchUser]);

  return (
    <UserComponent
      user={user}
      errorMessage={errorMessage}
    />
  );
};
```

Custom Hookは任意のcontainer/component層でデータとロジックを注入できます。
自分はいつもcontainer層で注入します。


Reduxを使うならアプリケーション全体で、Stateの管理方法をReduxで統一したいと思ってた時もありましたが、最近は要件によってCustom HookとReduxを使い分けるのが良いかなと考えています。

Custom Hookの中でapiリクエストをする場合、middlewareを気にしなくて良いです。

今のところ上手くいってますが、同じ画面やドメインでCustom HookとReduxの両方を使わないようにしています。


## まとめ

Reactでは「ViewとStateの分離」をいくつかの方法で実現出来ます。

とりえあずReduxにすると大きく外すことがないですが学習コストやコード量が大きいので、Reduxでオーバーエンジニアリングになりそうな時はCustom Hookがおすすめです。

## 参考

[JavaScript: Reduxが必要なとき/不要なとき（翻訳）](https://techracho.bpsinc.jp/hachi8833/2018_03_13/53183)

[Learn React Context in 5 Minutes - A Beginner's Tutorial](https://dev.to/bobziroll/learn-react-context-in-5-minutes-a-beginner-s-tutorial-2n21)

[Fluxとはなんなのか](https://qiita.com/knhr__/items/5fec7571dab80e2dcd92)