web技術だけでモバイルアプリっぽい見た目を実現する(React+Redux+OnsenUI)

モバイルアプリのようなUIを実装したいなーと思い、[OnsenUIのサンプル](https://github.com/frandiox/OnsenUI-Todo-App)のtodoアプリと同じようなものをReactとReduxを使って作りました。

下記のようなUIパーツをもったアプリを作ってみました。

- ヘッダーのツールバー
- リスト
- サイドメニュー
- フッターのタブ
- モバイルっぽいページ遷移

成果物は[こちら](https://satoshikawabata.github.io/react-redux-onsenui-todo-app/)にあります。
ソースは[こちら](https://github.com/SatoshiKawabata/react-redux-onsenui-todo-app)に置いてあります。

デモのgif
![,2017-03-15 15.55.23.gif](https://qiita-image-store.s3.amazonaws.com/0/29966/ba78ec97-de30-3b99-619f-9280d7785a22.gif)

## ビルド環境
フレームワークにはReactを使いました。
[create-react-app](https://github.com/facebookincubator/create-react-app)を使ってreactアプリケーションの雛形を作りました。
webpackの設定に辟易していたので、こういったボイラープレートはさくっと作るレベルの開発の時には嬉しいですね。
[CSS Modulesには対応していない](https://github.com/facebookincubator/create-react-app#limitations)ようで、そこが玉に瑕。

## OnsenUI
UIライブラリは以下の基準で選びました。

- モバイルっぽい見た目と動きが実現できる
- Reactとの相性が良い(できればReactコンポーネントとして使いたい)
- 小さいUIパーツだけでなく、画面遷移・リスト・サイドメニューなどの大きなレベルのパーツも揃っている

[ionic](https://ionicframework.com/)、[Material-UI](http://www.material-ui.com/#/)、[jQueryMobile](https://jquerymobile.com/)、[UIkit](https://getuikit.com/)なども見ましたが、今回はモバイルUIに特価している[Onsen UI](https://onsen.io/)を選びました。
OnsenUIは生のJSだけでなく、React、Angular、VueなどのJSフレームワークに対応しています。
[リリース頻度](https://github.com/OnsenUI/OnsenUI/releases)も高いのでちゃんとメンテナンスされていそうです。
Reactで使うには[react-onsenui](https://onsen.io/react/)というプラグインを使う必要があります。

### OnsenUIは結論から言うとかなり使いやすかった
- パーツがコンポーネント化されてReactナイズされていて、簡単に使用することができた
- コンポーネントの粒度が結構大きいので細かい所までカスタマイズするのには向いてなさそう
- だけど、ざっくりアプリっぽい画面遷移を作りたいときには便利
- [Reactのドキュメント](https://onsen.io/v2/docs/react.html)が充実している(実装している時はこのページを常に参照していました)
- [CSSの実装例](http://components.onsen.io/)も豊富
- 最悪[ソース](https://github.com/OnsenUI/OnsenUI)を読めば実装がわかる
- 今回はほぼCSSで手直しすることなく実現できた

### Navigator, Splitter, MainPage, SubPage等の構造

主要なコンポーネントの構造はこのようにしました。

```html
<Navigator>
  <Page>
    <Splitter>
      <SplitterSide>
        <SideMenu />
      </SplitterSide>
      <SplitterContent>
        <MainPage />
      </SplitterContent>
    </Splitter>
  </Page>
</Navigator>
```

## Reduxでfluxっぽく
ReactとReduxはかなり浸透しているのでもはや黄金コンビではないでしょうか。
導入については[こちらの記事](http://qiita.com/erukiti/items/e16aa13ad81d5938374e)を参考にしました。
ReduxをReactで使うためのプラグインに[react-redux](https://github.com/reactjs/react-redux)を使いました。

Reduserからアプリ全体のstateをpropsとしてComponentが受け取り、それを元にUIを表示させます。
Componentからのイベント(タップやキーボード入力)を受け取って、ActionCreatorでActionを生成させてReduserへとdispatch(伝送)します。

相関イメージ図
![スクリーンショット 2017-03-15 15.47.04.png](https://qiita-image-store.s3.amazonaws.com/0/29966/0d8fdb7e-913e-b143-8dbc-e814b650bfb3.png)
使ってみてざっくりと下記のような印象でした。

- reactのstateを外部管理することで、アプリケーション全体のstateとして管理できる
- 「子コンポーネントから親コンポーネントに何か伝えたい時 → actionを投げる」みたいな使い方(合ってんのかわからんけども)
- stateを複数コンポーネントで共有したい時にreduxのstoreを使う

どのタブを開いているか、サイドメニューが開いているかどうか、といった表示のためだけの情報もstoreのstateに載せましたが、本当は分けるべきなのかなーと思いつつ良い解を見つけられませんでした。
もしかしたらreduserをある程度の粒度(ページ単位?)で切り分けるのがいいのかもしれませんが試してないです。

### Reduxで非同期通信
Reduxで非同期通信をいい感じに扱うために今回は[redux-thunk](https://github.com/gaearon/redux-thunk)というミドルウェアを使ってみました。
作ったサンプルでは非同期通信は発生しないのですが、localStorageにアクセスするところを非同期APIに見立てて使っています。
他にも[redux-saga](https://github.com/redux-saga/redux-saga)というのもあります。
下記の実装例のように、ActionCreatorでAPI.getTodoというPromiseを返す非同期処理をthenで受けてからdispatchを使うことができます。

```js
class ActionCreator {
  getTodo() {
    return dispatch => {
      Api
        .getTodo()
        .then(res => {
          dispatch({
            type: ACTIONS.GET_TODO,
            todoList: res,
          });
        });
    };
  }
}
```

## 所感
久しぶりにReact書きましたが、やっぱり書きやすかったです。
stateとpropsドリブンでDOMを変更していくようにして、直接DOMを操作しないようにしてライフサイクルから逸脱しないようにすれば脱jQueryできて幸せになれそうです。

あと、この作り方を元に、Cordovaとか使ってweb技術だけでモバイルアプリが作れるか試してみたいです。
[こちらの記事](http://qiita.com/hal_sk/items/cdc459fd639a736bccc3)にそのまんまやり方が書いてあるので、それを実践してみたいです。