React way at Eight
2016/04/21
@halhide
自己紹介
• 大熊秀治(@halhide)
• Sansan株式会社
• 2014年1月にJOIN。もう3年目です。
• 名刺管理アプリEightのwebよろず屋です
• APIからUIまで何でもやってます
今日は現場よりの話
をします
Agenda
• Why React?
• How React & ride on rails?
• Componentをどう作るか(教訓)
• Redux(現場からの報告)
Why React?
2015/11月までのEight UI
http://contents.8card.net/blog/2013/130624.html
Backbone.js + coffeescript
railsとの相性が良いという触れ込
みで採用した(らしい)
端末アプリ向けのAPI開発がメイ
ンタスク
フロントエンド得意なエンジニア
がいたわけでもない
2014年頃から機能が増え始めた
メッセージ、お知らせ、レコメン
ド表示、、、
絡みあうModelとViewとjQuery
聞こえてくる悲痛な声
バグのモグラ叩き
どうにかしたい…
でもWeb画面が大きく変わらない
と手は入れらんない…
時は、2015/(7|8)月
2015/12月
公開プロフィール機能リリース
&
デザイン刷新
デザイン&画面構成大幅変更
納期短い
サーバ側も同時開発
とはいえチャンス到来
大鉈を振るえる
React
• Viewライブラリ
• 小さいものを使いたかった
• はまった時のworkaround見つかるか不安
• Delivery最優先のプロジェクト
• (なぜredux使ったし)
• 仮想DOM
• 細かいDOMの差分更新を考えなくても良い
• Viewの差分更新回りは苦い思い出しかない
• 技術記事もググれば出てくるし、自分の観測範囲において採用しよ
うかな、という話をちょいちょい耳にした
• 自分が使ってみたい :smile:
Eight UIの未来はReactに
ある!
How React?
環境
• ビルド
• ES2015で書いてbrowserify+babelでビルド
• タスクランナー
• gulp
• ES2015を変換したり
• webフォント作ったり
• テスト
• mocha、chai、sinon
• 最近はComponentのテストに airbnb/enzyme 使い始めた
• https://github.com/airbnb/enzyme
• Eslint
• Styleguide
• sapegin/react-styleguidist
How to ride on rails?
• jsのビルドに必要なソース
• RAILS_ROOT/front
• ビルド結果
• RAILS_ROOT/app/assets/javascripts
• fingerprintの為にやってるようなもんです
• ビルドが走るタイミング
• 開発中
• 変更をwatchして差分ビルド
• prodocutionデプロイ
• railsのprecompileの前にjsのビルドを走らせる
Q:railsとの関係はいつ
まで続けますか?
A:もうちっとだけ続くん
じゃ
railsに乗るデメリット
• コンポーネントの開発するのにrailsアプリが必須
• npmとgulpで完結させたい
• railsのviewがないと動作確認も厳しい
• 特定のViewに依存しない共通コンポーネントを作る時に特に辛い
• デザイナがローカルでデザインチェックするときにrails環境も
作らないといけない
Styleguide Driven Development(言い過ぎ)
• https://github.com/sapegin/react-styleguidist
• React Componentのstyleguide
• 差分があったcomponentだけhot reloadで再描画
• exampleをブラウザ上で直接いじれるので、簡単な動作確認もそこで可
能
• componentのみの開発がrailsのviewと独立できた
Styleguideデモ
デモその2
それでもまだ
• デザイナさんがUIをローカルでチェックしたい時にrails環境を
作らないといけない
• gulpでビルドしたものを更にrailsでassets precompileする非効率
• 世の中知見が出てきているので、railに乗り続けるか降りるかは
再検討したい
Componentをどう作るか?
失敗
• 再利用したいのに再利用できない
• 本当に同じコンポーネントですか?
事例:画像選択コンポーネント
• 要件
• 画像をローカルから選ぶ
• デフォルト画像から選ぶ
• 別要件で使うときに二つの
理由で使えなかった
異なる処理がまとまっていた
• 画像選択&保存が一つの同じコンポーネントで実装されていた
• 「保存」ではなく「選択」のみで十分だった
• サーバへのUPは別の処理でまとめてやる要件で使えない
• 画像選択コンポーネントを利用する画像保存コンポーネントと
いう構成だったらよかった
選択対象の画像が状態になっていた
• デフォルト画像はComponent初期化時にサーバからDLされ、コ
ンポーネントのstateで管理されていた
• デフォルト画像を変えたい時にpropsで変えられない
• 表示対象は外から制御できるようになっているとよかった
事例:プロフィールコンポーネント
• 要件
• ユーザのプロフィール情報を表示する
• 自分のプロフィールの時は編集アイコンが出る
• プレビューの時は最低限の情報だけ表示する
• 実装
表示と制御は分ける
• フラグ制御の闇
• 見た目が同じだが、微妙に異なるものをフラグ制御で一つのコンポー
ネントでコントロールしようとした
• コンポーネントをそれぞれ作ってる時間もなかった
• 最初はフラグはなかった
• 編集用が追加され
• 公開用が追加され
• プレビュー用が追加され
教訓1:stateは極力持たない
再利用を阻害する
• stateは極力持たない
• 表示に必要な情報はpropsでコンポーネントの利用側から渡す
• stateとして持つのは、コンポーネントの見た目/振る舞いを実現するた
めに必要なもののみ
• Ex.画像選択コンポーネント
• props: 表示する画像(array)
• state: 選択されている画像のindex
• <ImageSelector images={images} defaultSelected={0} />
教訓2:アプリの処理と見た目
の処理を分ける
再利用を阻害する
• アプリの動きと直接結びつく処理は持たない
• コンポーネントはonXXXで各種イベントハンドラーを提供するだけ
• アプリとしての動作は、propsでonXXXに設定する
• Ex.画像選択コンポーネント
• onSelect: 選択された画像情報を引数でもらう
• uploadImages: 引数でもらった画像をサーバに保存する
• <ImageSelector onSelect={uploadImages} />
教訓3:見た目の制御とアプリ
の制御を一緒にしない
誤った再利用を防ぐ
• 表示モードの “選択”と”描画”は分ける
• “見た目”が同じものをコンポーネント化しておく
• アプリの状態ごとにパーツを組み合わせてコンポーネントを作
る
• 編集用コンポーネント
• プレビュー用コンポーネント
• …用コンポーネント
• アプリの状態に紐付いて適切なコンポーネントを上位コンポー
ネントで”選択する”
Redux
現場からのレポート
http://staltz.com/unidirectional-user-interface-architectures.html
https://code-cartoons.com/a-cartoon-intro-to-redux-3afb775501a6#.qeky9us66
Eightでの使い方(移行中)
Store = Client側のDB
• 例:ユーザ情報
• friendはすでに自分とフレンドに
なっているか?
• /users.json で取得
• 例:article情報
• author:投稿者
• is_liked: 自分が「いいね」している
かどうか
• /articless.json で取得
単純なケース
• UserとArticleに依存関係がなければこれでも良い
ちょっと思い出してみてくだ
さい
複数のAPIが返す情報に関連がある場合
• ユーザ一覧画面から’tanaka’さんにフレンド申請を出したら、ポ
ストの方でもそれがUI上に反映されて欲しいですよね?
• users_reducerでfriendの値を更新
• articles_reducerでfriendの値を更新
• xxxx_reducerでfriendの値を更新
• yyyy_reducerでfriendの値を更新
サーバサイドで設計するときによ
く言われることを思い出す
同じ情報を複数箇所に置かな
い
Eightでのreducer
Entity
• 情報そのもの
• {id: 1, name: ‘…’}, {id: ‘article1’, message: ‘xxx’}
• SQLで言えば一つのレコード
• entities/ 以下のreducerは一つのテーブルのデータ操作を担当す
る
Result
• アプリ内で表示するまとまった情報の集合
• コンテナコンポーネント(Smart Component)で表示したい情報
• ただし、Entityへの参照しか持たない
• articless: [{article: ‘article1’}, {article: ‘article2’}, … ]
• users: [{user: ‘1’}, {user: ‘2’}, ... ]
View(Provider)
Action実行時
• FRIEND_REQUEST
• entity/users_reducer
• 対象userのfriendを更新する
• usersのresultの方はいじる必要がない
• DELETE_ARTICLE
• entity/articless_reducer
• 対象idを削除する
• articless_reducer
• 対象idを削除する
• resultの方では単純にリストから消すだけ
reducerの品質を上げやすい
• Result、Entityと分けたことで、状態更新の処理が単純になった
• オブジェクトのマージ
• リストへの追加、削除
• テストも書ける
具体的にどうやるか?
Normalizer
• APIの結果をresultとentityに変換してくれるライブラリ
• https://github.com/gaearon/normalizr
• 直接使わずに、entityをモデルに変換するようにラップしたもの
を利用
Normalizer
https://github.com/gaearon/normalizr
Immutable.js
• Result、Entityと分けたことで、状態更新の処理が単純になった
• オブジェクトのマージ
• リストへの追加、削除
• Immutable.jsの得意領域
• merge、update、add、deleteなどのオブジェクト、集合操作のAPIが定
義されている
• 変更すると新しいオブジェクトになる -> Reactの差分検知と相性が良い
Immutable.Record
• Entityに利用
• 許可されたカラムと、default
値を定義できるクラス
• getterも定義できる
• 普通のobjectのように参照で
きる
• get(‘message’)とかやる必要なし
• どんな属性を持ちうるか悩ま
ないで良い
PropTypeが明確に
• オブジェクトっぽく参照でき
るので、PropTypes.shapeの
validationができる
• Styleguideで、わざわざ
Immutableオブジェクトを作ら
ずとも表示を確認できる
夢
• Record定義から、entityのreducerを生成したり、、、
• PropTypeを生成したり、、、
• Action周りが良い感じになって、、、
以上、現場の大熊が報告しま
した
ちょっとだけ宣伝
本日の懇親会で使用しますので
事前のご登録をお願いいたします
2.名刺を撮影 3.登録完了1.アプリをDL
お手元のチラシを参照下さい
Sansanでは
※javascriptといえばサイですよね
とにかく仲間を募集してます!
http://jp.corp-sansan.com/recruit/job/engineer_ruby.html
(ruby採用だけどjs書ける人も募集中🙏)

React way at_eight