Reactにおける状態管理の方法を考えてみる 前編

はじめに

三菱総研DCSのフロントエンド技術者の里屋です。

私は普段、React/Reduxを用いたフロントエンド開発や技術調査の業務を行なっています。その他に社内向けにReact/Reduxの研修講師を担当しています。
その際に、「Reduxがよく理解できない、難しい」といった意見や「Reduxを使用せず、React単体でできないか」という要望が挙がることがあります。
ReactとReduxはセットで使用されることが多く、弊社でもフロントエンド開発で採用していますが、 2019年2月にリリースされた「ReactHooks」により、React単体でもReduxと同じような振る舞いを実現することができるようになりました。

今回は、状態管理を行うReduxの導入背景から、Reduxの代わりとして状態管理を行う ReactHooksの機能についてを説明していきたいと思います。 また社内研修の講師を経験して、私が感じたReduxの学習コストが高い理由についても述べたいと思います。

Reduxを導入する理由

Reactのアプリケーションの開発では、画面への描画を行うコンポーネントを実装していきます。 コンポーネントはその他に、計算結果の数値や入力された文字といった状態(state)を保持することが可能です。 アプリケーションの規模が大きくなっていくと自然とこのコンポーネントが増加していき、 下記のような問題が発生していきます。

  • どのコンポーネントが何の状態を保持しているのかわからない
  • コンポーネント間の状態の受け渡しの記述が増えていく(バケツリレー)

これらの問題を、状態管理を行うライブラリ「Redux」を利用することで防ぐことが可能です。
Reduxは状態を「Store」と呼ばれる場所に一括して保持されます。 そのため各コンポーネントは、必要に応じてStoreから状態を直接取り出すことが可能になり、 コンポーネント間の状態の受け渡しを削減することができます。 またReduxは単方向のデータフローを採用しているため、状態の変更は必ず「Action」と呼ばれる場所を起点に行われ、予期せぬ箇所からの状態の変更を妨げることが可能になります。 以上の理由から、Reactのアプリケーションでは状態管理を担うReduxが導入されることが多くなっています。

ReduxをReactのアプリケーションに組み込む明確な基準はありませんが、 個人的にはあらかじめ下記のような構想がアプリケーションにある場合、最初からReduxを導入した方が良いかと思います。

  • 画面遷移が多い
  • 複数の画面で同じ状態を参照することが多い(ログイン状態やアカウント情報など)
  • 機能拡張を行っていく可能性が高い

Reduxの学習コストが高いと言われる理由

Reduxを研修等で教えていて、Reduxの学習コストが高いと言われる理由について 私が感じた点は以下の2点です。

  1. 記述量の増加
    React単体であれば、状態の宣言から変更ロジックまでの全てを1つのファイル(コンポーネント)に収めることができます。
    しかしReduxを利用した場合はデータフローに従う必要があるため、状態変更を発行するためのActionの追加、状態を変更するためのreducerの追加、
    状態をブラウザに表示するためのコンポーネントの修正、Reducerが増えた場合にはStoreへも追加修正が必要となります。 Reduxを使い慣れたとしてもこの作業は面倒と感じてしまうので、初学者ならよりハードルの高い作業になると思います。

  2. Reduxライブラリ内の処理のブラックボックス化
    Reduxを用いたReactアプリケーションは下記の流れで状態を変更していきます。

    ① UI上でボタンクリック等のイベントが発生
    ② ActionでStoreに状態の変更内容を発行
    ③ Storeから現在の状態とActionから発行された変更内容を、reducerに渡す
    ④ reducer内で状態を変更し、変更後の状態をStoreへ渡す
    ⑤ 変更後の状態を受け取ったStoreは、コンポーネントにその状態を渡す
    ⑥ 新たに状態を受け取ったコンポーネントは再描画を行い、ブラウザの表示内容を変更する

上記の②③④⑤がReduxの役割です。大まかな流れは理解できると思います。 しかしこの中で③などのStoreが関わる処理はReduxのライブラリ内で行われています。 また「react-redux」や「redux-actions」などのRedux周辺ライブラリを使用してコードの簡略化を行なっているため、 説明をしているとどうしても「こう書けば良い」といういわゆる”おまじない”の箇所が多くなってしまい、処理を追うのが難しくなってしまいます。

ReactHooksを用いた状態管理の方法

以前まではReduxの学習コストが高いとはいえ、状態管理が明確になるためReduxを導入していました。 しかしReactの新機能「ReactHooks」を使用することにより、 先述したReact単体での実装上の問題を解決することができるようになりました。 具体的にはReactから提供されている以下の機能を使用します。

  1. ContextAPI
    ContextAPIではProviderとConsumerという機能が用意されています。Providerは状態を各コンポーネントへ提供し、 各コンポーネントは必要に応じてConsumerを利用して状態を受け取ることができます。 そのため、いちいちコンポーネント間で状態の受け渡しの記述を書く必要が無くなります。
    さらにReactHooksの機能の1つ「useContext」を使うことで、 状態を受け取る側(Consumer)のコンポーネントの記述を簡略化することが可能になりました。

  2. useReducer
    こちらもReactHooksの機能の1つです。useReducerは状態と状態の変更内容を受け取り、変更後の状態を返す仕様になっています。 ほぼReduxのreducerと同じような動作を行います。useReducerを使用すると、状態の変更の記述を特定の箇所にまとめることができます。

これらの機能を組み合わせることで、Reduxライクな実装を行うことができます。

まとめ

今回はReduxの必要性やReact単体での実装方法について説明しました。 次回はContextAPIやuseReducerを実際に使用してサンプルアプリを開発し、 Reduxを利用しないことのメリット、デメリットなどを考察してみたいと思います。