React/Reduxを管理画面に使った話

# 自己紹介

- [@adwd118](https://twitter.com/adwd118)
- [adwd](https://github.com/adwd) :octocat:
- 株式会社ビズリーチ
    - スタンバイ事業部
- Web歴1年
- サーバーサイドエンジニア？

-----

# React/Redux入門やりました

- :link: [speakerdeck](https://speakerdeck.com/adwd/redux-introduction)

![スクリーンショット 2016-07-07 18.03.28.png](https://qiita-image-store.s3.amazonaws.com/0/59312/729c0ec4-873c-3759-d4d5-17fa95a81583.png)

-----

# 求人検索エンジン スタンバイ

-----

# スタンバイ

![スクリーンショット 2016-07-04 18.07.31.png](https://qiita-image-store.s3.amazonaws.com/0/59312/1661f3f6-a5cc-e182-e49d-454533c09551.png)

-----

# スタンバイ

![スクリーンショット 2016-07-07 18.05.46.png](https://qiita-image-store.s3.amazonaws.com/0/59312/676fc0d3-94c4-8528-40da-763625b87785.png)

-----

# :exclamation: __社内向け管理画面__

- スタンバイの__社内向け管理画面__でReact/Reduxを使った
- スタンバイ本体ではない
    - SPA的要素はあまりない
    - パフォーマンス、セキュリティなどそこまで気を使ってない

-----

# コンテンツ

- React/Fluxについて
- 管理画面の構成
- React/Reduxを使った感想

-----

# React

![react.png](https://qiita-image-store.s3.amazonaws.com/0/59312/77cc00cc-8b59-2c11-c738-114282275701.png)

-----

# React

- javascriptでUIを構築するライブラリ
    - （MVCのV）
- コンポーネント指向
- js-centric
    - ほぼjsの中で完結する
- Flux
    - 単方向データフロー

-----

# Flux

- ViewはStoreからデータ（状態）を受け取って表示するだけ
- Storeの状態を変更するのはActionのみ

![flux-simple-f8-diagram-with-client-action-1300w.png](https://qiita-image-store.s3.amazonaws.com/0/59312/656e36a0-dba7-f38c-5c16-ee06d564651e.png)

-----

# Flux

- わかりやすい
- ActionとそれによるStoreの変化を追跡すれば良い

![flux-simple-f8-diagram-with-client-action-1300w.png](https://qiita-image-store.s3.amazonaws.com/0/59312/656e36a0-dba7-f38c-5c16-ee06d564651e.png)

-----

# サーバーサイドっぽい？

- View = html or json
- Store + Dispatcher = DB
- Action = リクエスト
- サーバーサイドエンジニアが片手間でフロントを構築するのにも向いている

![flux-simple-f8-diagram-with-client-action-1300w.png](https://qiita-image-store.s3.amazonaws.com/0/59312/656e36a0-dba7-f38c-5c16-ee06d564651e.png)

-----

# 社内向け管理画面

![スクリーンショット_2016-06-22_17_39_13.png](https://qiita-image-store.s3.amazonaws.com/0/59312/0302c371-9640-10bd-282a-1125dabb602d.png)

-----

# 普通（？）の管理画面

- なんかのリストを表示
    - そこにアイテムを追加・編集・削除
    - モーダルダイアログだしたり
- たまにグラフを表示したり
- **SPAではない**
    - けどReact/Redux使っちゃった :joy:

-----

# 管理画面にReact?

- 2015年末の状況＋勢い :innocent: 

<dl>
  <dt>jQuery</dt>
  <dd>でっかいフォーム・表がつらい :skull: </dd>
  <dt>Angular 1</dt>
  <dd>そのうち全然違う2が出る</dd>
  <dt>Angular 2</dt>
  <dd>まだbeta</dt>
</dl>

-----

# 構成

- React, Redux
    - react-redux, react-router, redux-form, redux-thunk
- babel
- browserify
- mocha, power-assert, enzyme, sinon, nightwatch
- react-bootstrap, AdminLTE
- バックエンド
    - Scala/Play Frameworkでjson APIサーバー

-----

# React/Reduxを使った感想

-----

# React/Reduxを使った感想

- 注意: 個人的な感想です！

-----

# Pros/Cons

-----

# Pros: わかりやすい :joy:

- Reactコンポーネント
    - コンポーネントの中にhtmlのテンプレートとロジック(`state`, `props`)がある
    - jsファイルを一つ見ればそのコンポーネントがわかる

-----

# Pros: わかりやすい :joy:

- Reactコンポーネント
    - 管理・操作するのは`state`, `props`でただのJSONオブジェクト
        - DOMを意識しなくて良い :innocent: 
    - js(+css)を見てJSONを管理するだけ...ただのプログラミングだこれ！
 
-----

# Pros: わかりやすい :joy:

- Flux/Redux
    - 単方向データフロー
    - Action -> Reducer -> Store -> View
    - Actionとその結果のStoreを気にすれば良い
    - Redux DevToolでAction,Storeを時系列に表示、リプレイも可能
    - 書き方がほぼ決まっている
    
-----

# Cons :cry: 

- 書くコードの量は多い(主にRedux)
- jsxがキモい・・・？
    - すぐ慣れる、むしろ好き
- npm, browerify(webpack)の知識
    - 最近sourcemapが壊れててヤバイ :joy:
- React/Reduxの学習コストは少なからずある
    - jQuery < React/Redux < Angular ?

-----

# Cons :cry: 

- Reactでフォームが楽に書けるかというとそうでもない
    - [redux-form](https://github.com/erikras/redux-form)でそこそこ楽になった
    - フォーム周りはAngularとかのほうが楽？

すげー辛い！ってことはそんなにない :innocent: 

-----

# Reduxいらなかったかも

-----

# Reduxいらなかったかも

- SPAではないので、アプリケーションとして管理すべき状態はあまりない

-----

# Reduxいらなかったかも
- _Fluxが必要かどうかわからないなら、必要ではない_
    - by React初期からの開発者: Pete Hunt
- _Reactだけで問題がないなら、Reduxは必要ない_
    - by Redux開発者 Dan Abramov
- 人の話は聞こう :joy: 

-----

# Reduxいらなかったかも

- 状態を管理するコンポーネント、表示を行うコンポーネントの分割をちゃんとやればReact, React-routerで十分だった
- [Presentational and Container Component](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0)
- とはいえRedux Devtoolは便利

-----

# Storeの設計ミス

-----

# Storeの設計ミス

- URLのパスとStoreの分割を一致させた
    - `users`, `user/:id`, `user/:id/edit`

```json
{
   "listUsers": ...,
   "showUser": ...,
   "editUser": ...,
   ...
}
```

-----
# Storeの設計ミス

- チームでの分業には良い（？）
- 同じ情報を複数の場所で持ってしまう
- 不必要に巨大なStoreに :joy:

-----

# Store = 状態 + エンティティ

<dl>
<dt>状態</dt>
<dd>例: inputに入力している文字列</dd>
<dt>エンティティ</dt>
<dd>例: APIから受け取ったデータ</dd>
</dl>

-----

# これを...

```json
{
  "todo": {
    "edit": { "text": "sh", "user": "tom" },
    "list": [
      { "text": "eat", "user": "tom" },
      { "text": "sleep", "user": "tom" }
    ]
  }
}
```

-----

# こうじゃ！

```json
{
  "todo": {
    "edit": { "text": "sh", "user": "tom" }
  },
  "entities": {
    "todo": {
      1: { "text": "eat", "userId": 1 },
      2: { "text": "sleep", "userId": 1 }
    },
    "user": {
      1: "tom",
      2: "bob"
    }
  }
}
```

-----

# エンティティの管理

- [normalizr](https://github.com/paularmstrong/normalizr), [reselect](https://github.com/reactjs/reselect)
    - REST APIからのデータを正規化する
    - 参照: Dockerの中の人が書いた[gitbook](https://tonyhb.gitbooks.io/redux-without-profanity/content/ch1-state-management.html)
- 毎回API叩くわけにもいかないのでキャッシュ的な実装も必要？
    - 社内向け管理画面なんで毎回叩いてます :joy:
- Relay, Falcor
    - 大規模なら検討の価値アリ？
- このへんのベストプラクティス教えて下さい :pray: 

-----

# コンポーネントの粒度

-----

# コンポーネントの粒度

- 初期は1画面1コンポーネントで作っていた
- 巨大な`state`、巨大な`props`
    - それに合わせた何でも返すAPI
- 後から見ると読みづらくメンテナンス性 :arrow_down: 
- 再利用性のないコンポーネント、API

-----

# コンポーネントの粒度

- 意味がある単位で小さく分ける
    - それに応じた小さなAction, Reducer, API
    - 小さく分けて組み合わせる（Unixっぽい）
- 読みやすさ、テストの容易さのためもなるべく小さいほうがよいのでは

-----

# テストライブラリの充実

-----

# テストライブラリの充実

- enzyme, sinonを使えばコンポーネントに対するユニットテストは簡単に書ける

参照: [ReactでTDD（テスト駆動開発）を始めよう : 環境構築からテスト作成、機能実装までの詳解ガイド](http://postd.cc/getting-started-with-tdd-in-react/)

-----

# enzyme テストコード例

- インスタンスメソッドの実行、`state`変更の確認

```js
it('adds items to todo list', () => {
  const wrapper = shallow(<TodoList />)
  wrapper.instance().addTodo('learn enzyme')
  assert.deepEqual(
    wrapper.state('todos'), ['learn enzyme']
  )
})
```

-----

# enzyme テストコード例

- イベントのシミュレート、コールバック関数実行の確認

```js
it('callback should be called', () => {
  const onButtonClick = spy()
  const wrapper = shallow(
    <TodoItem onRemove={onButtonClick} />
  )
    
  wrapper.find('input').simulate('click')
  assert(onButtonClick.calledOnce)
})
```

-----

# まとめ

-----

# まとめ

- 社内向け管理画面でReact/Reduxを使った
- よかった
    - 主にわかりやすさ
    - サーバーサイドエンジニアでもいける
- とはいえ書くコードは多めになる

-----

# まとめ

- React
    - コンポーネントを小さく分けて可読性・テスタビリティを上げる
    - テストはenzymeで書きまくれる
    - [React-Bootstrap](https://react-bootstrap.github.io/) + [AdminLTE](https://almsaeedstudio.com/preview)で見た目が整った画面をサッと作れた
- Redux
    - 単方向データフローのわかりやすさ
    - Storeはエンティティをキャッシュする役割もあることを考慮して設計する
- まだまだ学ぶことは多い・・・！
    - けど面白い :joy: 

-----

# おわり