React.jsは難しいのか?Redux代替にEventEmitterを使用して難易度を下げる

react_icon React.js

この記事は、プロジェクトにReactを使用しようと検討中の方で、「Reactって難しいのでは?」「Reactと一緒にくっついてくるReduxって不可欠なの?」などという懸念を持っている方に向けて記載しています。

私はReactを使用して2020年11月時点で9ヶ月ほど個人的なプロジェクトの開発をしている者です。現時点で上記の問に端的に答えるとするなら、Reactは難しくないし、Reduxは使いたくなければ不可欠ではないです。素のReactの仕様自体は、stateとprops、renderをうまく管理すれば極めてシンプルに作り込みができて、使い勝手はよい方です。多くの人がReactは難しいと感じるのは、Reactとセットで導入されるReduxというライブラリが元凶になっていると想像しています。

Reduxの使用で苦しめられた私が行き着いたのは、Reduxの代わりにEventEmitterを使用してアーキテクチャを組む手法でした。その手法を紹介します。

Reactはいいが、Reduxがイヤだ

ReduxとはReactの状態管理用ライブラリとしてよく使用されており、使用した場合のメリットとして以下が挙げられています。

  • propsのバケツリレーを回避できる
  • データフローが綺麗になる
  • 状態の一元管理が出来る

これが本当なら素晴らしい。しかし、この図で喜んで、いざReduxを始めようとする人が最初に目にするであろう概念図。

こういうのとか、

Redux概念図 https://itnext.io/integrating-semantic-ui-modal-with-redux-4df36abb755c

こういうのとか、

Redux概念図2 https://dev.to/radiumsharma06/abc-of-redux-5461

これを見て、私は思いました。

…???

…何かハコ多くない???

…線多くない???

いや、propsのバケツリレー、回避したいよ?確かに。データフロー、管理したいよ?でも、なんかそれだけの為に、こんな色々やんないといけないの?

公式ホームページとか見ていると、チュートリアルの簡単なToDoリストの為に何行書いてるのってくらいいっぱい書かないといけないようです。これは「小規模アプリだとメリットが小さく感じられる」「大規模アプリならメリットを享受できる」という論理で説明されているようですが、私がPMなら大規模アプリみたいに沢山の人が関わらないといけないようなものに小難しいライブラリ導入して、学習コストを上げるようなことはしたくないです。大規模プロジェクトに新規メンバがやってきて(皆が皆そんな賢くない)、いきなりReduxの説明が始まったら、「あ、Reactって難しいんだ」って思われる。Reactは使いやすいフレームワークですが、Reduxのせいで変に敷居を挙げられてしまうのは残念です。

今回はReduxの紹介記事ではないので、Reduxの詳細は記載しませんが、上記の概念図に紐付いてくるReducerとか、storeとかにまつわる実装が本当に楽しくない。具体的には以下の点が自分の中で納得のいかないところでした。

  1. Reducerの実装が面倒くさい。
  2. Reducerの中に全然関係のない処理用のSwitch文が一様に並ぶのが、なんか気持ち悪い。
  3. Storeの変更があった時に、コンポーネント内に定義された任意のメソッドをそのまま呼び出すような構造にできない(componentDidUpdateとかのライフサイクルメソッドを経由しないと呼び出せない)。
  4. 3の理由から、IndexedDBとの相性が良くないように感じる

特に3のデメリットが大きくて、具体的には、単純な画面更新等ならStoreが変わるたびに上手く同期させることができるんだけど、Storeの変更を察知してコンポーネントが持つやや複雑な処理を反映してから画面更新するような場合に、汎用のライフサイクルメソッドを経由しないと実現出来ない。ライフサイクルメソッドはStore更新時以外も呼び出されるので、Store更新時とそうでない時を区別するような条件分岐を入れたりしないといけなくて、コードがかなり汚くなりました。自分のプロジェクトで使ってみたのですが、3ヶ月くらいで使用をやめることにしました。

もっと真剣に使用法について改善検討すれば、少し楽になれる手段はあったのかもしれません。しかし私、そもそもそんな真面目な性分でもないし。せっかくライブラリを使うなら、そんな死ぬ気で汗かくやつより、もっとキャッチーなやつを使いたいじゃないですか、なんでライブラリ一つに血眼になって勉強しないといかんのだ。怠惰な私にはとても耐えられません。

代替ライブラリを探し出して分かるのは、同じように感じる人は結構いて、「Redux 代替」とかで検索すると結構出てきます。代替手段は一つではなく、useContextを使用する方法等もあるようですが、私が推すのは”EventEmitter”というライブラリです。

そもそも状態管理系ライブラリが必要なユースケースとは?

そもそも、Reduxのような状態管理系ライブラリに有り難みが出てくるのって、例えば以下のような構造になっている時です。

やや複雑な親子関係

上記の構造の時、GrandchildからChild2にデータを渡したい時に、状態管理系ライブラリ無しだと、propsを経由して以下のようにややこしい橋渡しをしてやらないといけません。

素のReactで親子間のデータの受け渡しを行う場合

それを、Reduxのような状態管理系ライブラリを導入すると、Store経由で以下のようなルートで直接データを受け渡せるわけです。

Reduxを用いてデータの受け渡しを行う場合の概念図

このコンセプト自体は非常にシンプルで受け入れやすいものだと思います。但し、前述したとおり、Reduxには特有の面倒くささや制約があるので、このコンセプトのままもっと簡単なライブラリに置き換えられたら、と考えました。その代替ライブラリとしていい感じだったのが、EventEmitterです。

EventEmitterライブラリ – Event駆動開発を可能にするライブラリ

EventEmitterはReactに限らず、JavaScriptで使用可能なNode.jsのライブラリで、使用法はシンプルです。EventEmitterの概念を自分の理解の限りで図にすると、以下のような感じになります(あんまり厳密ではない)。

EventEmitterの概念図

以下のような流れになります。

  1. Emitter(送信側)とReceiver(受信側)はEventEmitterの同じ実体を共有しておく
  2. Receiverは受信するイベントを指定し、Listenしておく。
  3. EmitterはEventEmitterライブラリを通じて、イベントを発行することができる
  4. イベントが発行されると、Receiverの側でListen時に登録されたメソッドが実行される

公式の実装サンプル(コメントは筆者加筆)は以下のようになっています。

非常にシンプルです。受信者がイベント名’event’を指定してイベントを.onでListenしておき、送信者が同名のイベントを指定して.emitで発行すると、受信者の側で登録されたconsole.log(‘an event occured’)が実行されます。

この例だと、受信者と送信者が同じファイル内に定義されているので、あまり実用的ではないですが、原理を理解するには十分です。

この仕組みを使って、Reduxで実現させたかった構造を、代わりにEventEmitterを使って作ることができます。つまり、このような形です。

EventEmitterを用いてデータの受け渡しを行う場合の概念図

上記の実装例として、以下のような画面を作ることにします。

分かりやすいように、概念図そのまんまの画面にします。上記の画面で、GrandChildのCountUpボタンを推すとChild2のカウンターが1ずつカウントアップします。同時にGrandChild側から文字列でメッセージもおくります。

まず、準備としてcreate-react-appを叩きます。

eventsライブラリを導入しておきます。

以下に実装を示します。

これでGrandChildのボタンをクリックすると、Child2のカウンタとメッセージ表示が変化します。

以上、今回は直接送信者から受信者にデータの受け渡しを行うサンプルを示しました。他にも、EventEmitterを使用すると、MVVMアーキテクチャのように、Modelのデータが変更されたら、そのModelを参照する各部品が(自主的に)表示を更新する、というような構造にもできます。

例えば、Web経由でデータを取得し、ModelでlocalStorageやIndexedDBの情報を書き換える→Modelでイベント発行→それらを参照する画面(View)が表示更新する、という構造も割と簡単に組めます。何より、イベントの追加に対する抵抗感が少ないです。

ViewとModel間でEventEmitterを使用してデータをやり取りする構造についても、追々サンプルを掲載していきたいと思っています。

→2020/11/23 MVC + EventEmitterで、Redux抜きでRedux公式サンプルのTodoリストを実装する記事を掲載しました。React.jsは難しいのか?Redux代替にEventEmitterを使用して難易度を下げる – ToDo List実装編

コメント

タイトルとURLをコピーしました