fluxパターンのシンプルな実装と解説:React 実践チュートリアル4

fluxパターンとは、要するにオブザーバーパターンの進化系の様なものである。

まず、オブザーバーパターンとは一言で言うと、データの状態の変化を検知し、その変更を受け取る仕組みのことである

データがあり、そのデータの変更が

の様な形で受け取っている時、それはオブザーバーパターンと呼んで良い。

fluxパターンとは、これにデータの流れ方のベストプラクティスを組み込んだものであり、
具体的には、データの変更要請 -> データの変更&保存 -> viewへの反映という一連の流れを分離し、かつ一方通行で行うべしというフレームワークである。

fluxパターンを使うと、データ層をviewから完全に分離することによって、複数のViewコンポーネント間での複雑なデータのやりとりがしやすくなるというメリットがある。

逆に言えば、シンプルなデータ構造のアプリケーションではfluxは必要ない。
あまり語られていないが、fluxを使うとどうしてもコードが複雑になるので、シンプルなデータ構造にはできるだけfluxは採用せず、component間でのpropsのやりとりにとどめておくべきというのもプロ目線でfluxを扱う上での重要な方針だ。

flux自体はすごくシンプルな発想だが、flux公式のこの図

がすごくわかりにくいので、fluxをなんだかすごい学習コストの高いものの様に勘違いする人が多い。
実際、これのせいで「最近のwebフロントエンドは難しい!」とか言われていたりするが、相変わらずwebフロントはそんなに難しい仕事では無いので安心してほしい。

結局はコードを見ないとよくわからないのである。では実際のコードで見てみよう

0. Memoアプリを作る(準備編)

今回は「fluxがなぜ必要なのか?」に焦点を当て、簡単なメモアプリを作っていく。

今回実装するのは次の機能

  • メモをinputに入力して追加ボタンを押したら下にリストが出る。

話をシンプルにするために変更・削除などの機能は無しとする。

前回の続きから、シンプルなコンポーネントを追加した状態でスタートする(react-bootstrapなんかもついでに追加しておいた)

http://localhost:3000/FluxSample.htmlにアクセスして次の様なページができればとりあえず準備は完了だ。

 

上のページはreact-bootstrapを使ってシンプルに作成した。
react-bootstrapはhttps://react-bootstrap.github.io/components.htmlにサンプルがあるが、bootstrapのパーツが全てComponentとして気軽に使える様になっており、今回の様なシンプルなアプリを試すには非常に便利だ。

ファイルはこんな感じに用意しておいた

 

1. fluxパターンを使わない実装

まずはfluxパターンを使わずに実装してみよう。

  • メモをinputに入力して追加ボタンを押したら下にリストが出る。

というのは、シンプルに実装するなら次の様な流れとなる

  1. Addボタンが押される
  2. InputのテキストをListに渡す

早速実装してみよう。

まずはAddMemoFormから

dom-parser内からShowMemoListComponentを探して、addMemoメソッドを呼び出す。
Reactのみでは親から子コンポーネントへのデータの受け渡ししかできないが、dom-parserを使えばこういった並列のデータ渡しもできるようになる。
Reactを使うときはAppといった巨大コンテナに包むのが普通と考えていた人はこのやり方も是非試して欲しい。設計の自由度が無限に広がるはずだ。

Reactにはrefという機能があり、renderメソッド内で

という様にref属性を使ってthis内の好きな名前のプロパティにElementをセットすることができ、

という様にしてElementが表示しているDOMを取得することができる。

次はShowMemoList.addMemoの実装だ。

this.state.memoを空の配列で初期化し、addMemoによってstate.memosにプッシュする。

あとはrenderメソッド内で現在のstateを表示しておく様にしておくだけだ。

2. fluxパターンを使わない実装の問題点

以上がReactのデータの受け渡し方の基本である。
コードもシンプルに収まるし、なるべくならこの実装で済ますのが基本だ。

しかし、追加、修正、削除、ソートなどの機能を追加していくには、問題が出てくる。

今回の場合、ShowMemoListComponent内でデータの操作をしているのが問題だ。

ShowMemoListは、本来メモのリストを表示するという機能のみに特化すべきであり、
渡されたデータが空白で無いか確認してmemosに追加するというデータ操作をしてしまっている。

これでは、’データの追加’という機能をShowListが持ってしまっており、より根本的な問題は正しいmemosの状態をShowMemoListしか知らないという問題である。

これは、こんな感じに現在のmemoを状態を表示するShowMemoSizeComponentを考えてみればわかりやすい。

早速実装してみよう。(一応ここまでのコードは4.3のタグで置いてある)

memosの本当の状態というのはAddMemoが実行された時(データ操作実行時)にしかわからないので、AddFormには次の様にShowMemoSize.addMemoを実行する行を追加することになる。

ShowMemoListComponentにアクセスしてmemosを取得することもできるが、その場合、データの同期という問題が出てくる。ShowMemoList内のmemosが変更されたことを知らなければShowMemoSizeはmemosの数を知ることができないのだ。

さらに、今回の実装は空文字の時に無視するという実装があるが、ShowMemoList内にその実装が漏れているため、空文字でもメモの数が増えて表示されてしまっている。

つまり、データの操作にまつわる実装を全てViewにも細かく実装しなければならず、バグの温床になりやすいのである。

この問題を解決するのがfluxパターンであり、この場合だとMemoActions.addMemosを発行し、それを各Componentが受け取るという仕組みを上の実装から分離するための仕組みである。

3. fluxの主要部品の解説とシンプルなflux実装

fluxというのは単なる概念であり、実装の仕方も複数ある。
だが、まずはきちんと概念を理解するために、一番複雑なfacebookの公式実装に近い形で自分で実装してみよう。概念さえわかれば後述するrefluxやreduxという現場で使われているライブラリでの実装もすんなり入っていけるはずだ。

再び、この図を見て欲しい

一つ一つ解説しながら実装していく

※ここまでのコードは4.3.1だが、npm install enum event-emitter –saveが必要

※ちなみに、以下の実装は[入門React]のコードをes6でわかりやすく、きちんと動く様に書き直したものである。シングルページではなく、あくまでReactをhtmlの一部として扱うように書いているので、facebookの実装よりシンプルになっている。

コードはこんな感じに置いてみた

 

■ Dispacher

Dispacherはdispatchとregisterの2つのメソッドを持ち、それぞれ

  • register: アプリケーションの初期化時にActionTypeに応じたStore内の対応メソッドを登録しておく。
  • dispatch: Action実行時、登録されたStore内のメソッドにActionで渡されたデータを渡す。

という役割がある。

es6で書くとこんな感じ。(この実装はサンプル用なので最低限)

シングルトンで実装するのでexportする時にnewしている。

dispatchの実装が少し長いが、パフォーマンス調整のための実装なだけで、registerで登録されたhandlers.callback (ActionTypeに応じてStore内のメソッドを発火)を実行しているだけである。

初期化処理は、コードの入り口に

こんな風に起動スクリプトを置いておく。

registerにMemoStore.addMemoを登録している様子が見えるが、これは上の図でいう右上の部分、
Dispatcher->callbacks->Storeを登録しているところである。

■ ActionCreators

ActionCreatorはその名の通りActionを作る場所だが、まずはコードを見ないとわかりにくい

これはComponent側から次のように呼び出される

上の図の左部分、ReactViews->UserInteractions->ActionCreator->Actions->Dispacherの実装がここに全てある。

Actionと呼ばれるものは

この部分で渡されているactionTypeとデータのセットである。これをDispatcherに渡すことで、Dispacherに渡して置いたStoreのメソッドが呼ばれる。

■ Store

Storeはデータの保存場所であり、唯一のデータ操作が可能な箇所である。

ActionCreatorからDispatcherを通じてStoreのaddMemoが呼び出されると、Store内でmemosのデータを変更し、変更されたことを通知する。

通知の仕方は一括してmemos全体であり、個別にmemoSizeを通知したり、変更箇所のみを通知したりはしない(することもできるが)。
MemoStoreはあくまで、memosの状態のみを管理し、memosが変更されたことをmemosごと通知する。

emitterというのはオブザーバーパターンでいうlistenerのことである。
addChangeListenerでstoreにコールバックを登録して置いて、Storeに変更が行われた際はそれをコールバックに渡す。

storeとviewの結びつけは以下のように行う

View内の余計なデータ操作が消え、memosの状態に応じてstateを変化させ、表示するというReact本来の姿に戻った。

AddMemoFormComponentもこのようにスッキリした見た目になっている。

4.reflux,reduxといったより簡単なフレームワークを使おう

上の実装は本当に簡単な生実装だが、実務で使うにあたっては必ずreduxかrefluxを使うことになるだろう。

そこで次回はrefluxについて、その次にreduxについての記事となる。

ここで簡単にそれぞれの特徴をまとめておくと

  • reflux:
    メリット::完全にアプリケーションから分離でき、複数のActionやStoreを気軽に設けることができる。また、シンプルな実装でわかりやすい。あまりfluxに慣れていないエンジニアが多く集まる職場向け
    デメリット:ドキュメントが少なく、人口も少ない。ネガキャンが多く今後淘汰される可能性もある(悲しい)
    また、プロジェクトが大きくなってくるとStoreが乱立して少々管理が面倒
  • redux:
    メリット:Storeがアプリケーション内に必ず一つであり、データ管理や設計がやりやすい。データの流れがシンプルになる。ドキュメントが充実。とにかく人口が多く、webでの情報も豊富。プラグインもようなものもたくさん出ている。
    デメリット:アプリケーションからの分離が難しい。シングルページアプリケーション向け(後の解説でこのブログのようなhtmlと共存する使い方でのreduxは解説する)。ドキュメントやサンプルコードが難解で離脱者が多数出る。オタクなプログラマーが集まる職場向け。

要するに私は個人的にreflux推しであり、このブログでもrefluxを推していくが、reduxの方が人口が多いのでしょうがなくreduxも解説する。

reduxは上のfacebookの生実装に近く、どうしてもコード量が多くなる。シンプルに一方通行のオブザーバーパターンが欲しいだけならrefluxで良い。

 

h-iwata

最近までReactでフロントエンド構築してました フリーランスなんでサーバーサイド、アプリ、ゲームなどなんでもやります。現在はunityでVRゲーム作ってます。 何か聞きたいことや頼みたいことがあるならコメント欄か、直接facebookまでどうぞ。

fluxパターンのシンプルな実装と解説:React 実践チュートリアル4」への2件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。