React.js + Bacon.js = 美味しい（と思いたい）


ついこの間、久しぶりに色々勉強して、AWS認定ソリューションアーキテクト（アソシエイト）に合格できました。
いやー、16kは懐が痛いので、これで経費でおります。

そんなことは置いといて、最近現場のほうで **やっぱりiPad/Androidでも使いたいよねー（社内システムを）**　という話が出てきました。わりと前から出てきてはいたんですが、保守コストかなり増えますよという言葉を盾にして躱してきましたが、ついに躱しきれなくなったので、とりあえずモックを作ってみることになりました。

既存のコードベースを利用したくなかった（一部以外は利用できそうもなかった）ので、じゃあこれ幸いと、やったことのなかったアーキテクチャにチャレンジしてみることにしました。

# React.jsとBacon.js
React.jsは言わずと知れたVirtualDOMのライブラリです。Bacon.jsは、RxJSがクローズドソース？だったころに挙動をみながら作られた、FRPライブラリです。

ReactがReactiveの略なら、FRPと相性いいんじゃないか、ということで、調べてみると幾つか実例がありました。

https://github.com/milankinen/react-bacon-todomvc

今回は、あくまでモックということだったのと、いきなりライブラリ使っちゃうとどう辛いのか、とかがよくわかんないと思ったので、pureなReact.jsとBacon.jsを組み合わせてみました

# やってみた
やってみたところ、それまでと勝手がかなり違って、慣れるまで大変でした。この構成を使ってみたときにハマったところや注意しないとならないところは色々あります。

## Bacon.jsの使い方がわかんない
FRPライブラリっていうものを使うのが初めてだったので、リファレンスと首っ引きになりました。というかなっても良くわかんないときがあったので、実際に動かしてみるのが一番です。

あと、↑の参考にしたリポジトリでは、 [Ramda.js](http://ramdajs.com/0.17/index.html)というライブラリが使われており、これの挙動を調べるのにもかなり時間を使いました。一気に新しいのを使いすぎると大変ですね。

## Propertyの構築
Bacon.jsには、combineTemplateという便利関数があります。

```JavaScript
var state = Bacon.combineTemplate({
  foo: Bacon.const(1),
  bar: Bacon.const(2)
})
```

こうすることで、propertyやstreamに値が流れてきた時点で、このstateというstreamに、combineTemplateに渡したオブジェクトをテンプレートとして、その時のproperty/streamの値が入ったオブジェクトが流れてきます。

概ね、React.jsのコンポーネントに渡すpropはこれになると思います。

## ネストしたproperty
SPAで色々な画面遷移とかがあると、条件に応じてネストさせたりといったものをしたくなります。

```JavaScript
var state = Bacon.combineTemplate({
  nest: {
    nested: Bacon.const(1) // ここ
  },
  bar: Bacon.const(2)
})
```

ただ、このようにネストできるのであればいいのですが、実際にはpropertyを生成するmoduleから生成したpropertyをトップレベルに並べるような感じが多くなるような感じがします。


```JavaScript
var nest; // これがfoo.toProperty()とかで生成されたpropertyと思いねえ
var state = Bacon.combineTemplate({
  nest: nest,
  bar: Bacon.const(2)
})
```

こんな感じにしたい場合は、このネストさせるpropertyもcombineTemplateで作る感じになります。

## Reactの更新頻度
さて、stateができて、 ```state.onValue``` で都度オブジェクトを受け取ってレンダリングをするわけですが、これで受け取るものは基本的にトップレベルのpropになります。

・・・ということは、毎回トップレベルの ```React.render``` を呼び出すということになります。

想像がつく通り、これはかなりパフォーマンスに効いてきます。実際に試してみた感じだと、10個くらいコンポーネントがネストしていると、iPadのWebViewだとちょっともっさりに感じます。
AndroidのChromeだとまだまだ大丈夫ですけど。

これをちゃんと解消するためには、componentShouldUpdateとかをちゃんと定義したり、毎回トップレベルから書き直さず、renderを分解して、適切なコンポーネントだけレンダリングされるようにする、などが必要かと思います。

## 非同期の取り扱い
Bacon.js自体は非常にajaxと親和性が高いんですが、実際には読み込み中だったらローディング状態にしたい！とかがあるわけでして。

Promiseを全部統合する、とかそういったことができればいいんですが、別々のmoduleになっていたりするとこれも難しいです。

全域で読み込み状態を同期させる必要がなければ（非同期のもの全部が終わったらローディングを解除、とか）、それぞれのpropertyの中にloadedとかの状態を入れてしまうのが、わりかし手っ取り早いです。無論これがベストではないとは思ってますし、無用なstreamの更新を招くので、これはこれでなんとかしないとならないとは思います。

# メリット・デメリットの総括
やってみてわかったメリット・デメリットを並べてみます。

まずはメリット。

* データの流れが完全に一方向になるので、Fluxが非常に実現しやすい
* 値を更新したら、stateやらなんやらやんなくてもViewが更新されて気持ち良い
 * 2 way bindingとかないので、非常にViewがシンプルになる
* 適切にモジュール化しないと自分もすぐ訳がわかんなくなるので、半ば強制的にモジュールの分割を考える

次にデメリット。

* そもそもFRP自体が盛り上がってきてはいるが、知らない人の方が多数
* つまり引き継ぎがものすごいしづらい
 * 保守の人にFRPバリバリな人が来るって考えるって・・・
* どういった流れでデータが更新されるかが、templateの構成によってはよくわからなくなる

他にもありますが、主だったものとしてはこれくらいかと思います。

ただ、個人的なデメリットは **引き継ぎがしづらい** ということです。自社のプロダクトとかならどうにでもなりそうですが、客先の社内システムのフロントエンド、となると非常にリスクが高いという・・・。

ただ、pureなReact+Baconをやってみた限りだと、Baconの部分を適切にラップできれば、かなりViewが簡略化でき、結果として理解はしやすくなるんではないかと思います。

特にオチはないんですが、もっと皆さんFRP＋Reactをやってみましょう。色々新しい世界が見えます。Reactじゃなくて他のVirtualDOM実装でも問題なくできると思います。
Angularはどうだかわかりませんが。