Rails4アプリのjQueryフロントエンドをReact.jsに置き換えるリファクタ活動を始めたので、これまでの理解をメモ
コードの臭い
- (jQueryによる)DOM操作・クラス操作の累積
- addしてremoveしてaddしてhide...本当に意図通り動いているのか、何か忘れてないか
- 特殊なDOM構成の存在を前提とした操作
- DOMがどこから操作されているのか不明
移行指針
- 問題の変換
- 状態変化に伴うDOM操作の処理を自分で計算せず、すべて
render
に押し付ける - HTML差分計算・再描画に関する問題・思考を最小化し、stateの操作に集中することでビジネスロジックの整合性を確認する
- 状態変化に伴うDOM操作の処理を自分で計算せず、すべて
- 双方向の思考を一応念頭に置く
- ボトムアップ、コンポーネント指向(?)
- 小さいコンポーネントを先に置き換えて、大きいコンポーネントでそれを使う
- トップダウン、インターフェース指向(?)
- 大きいコンポーネントを先に置き換えて、途中で出てきた再利用性の高い部分をどんどん別コンポーネントに分割する
- ボトムアップ、コンポーネント指向(?)
- ついでにいろいろできそうだが、最小の工数で移行する
入り口
- どこから導入すべきか
- 修正の必要が発生したが、DOM操作がすでに溢れ、次の修正でバグが入りそうな箇所
- DOM操作を追加・修正せざるを得ない箇所
- API経由の遅延描画に置き換えたい箇所
- 壊れていないものを直そうとしない
- もし静的解析で定量化できたら、複雑な箇所から置き換える
準備
-
gem 'react-rails'
-
Chromeプラグイン https://fb.me/react-devtools
- React関連の詳細なエラーの確認がしやすくなる
- Reactが生成しているDOMのstateなどの確認ができる
-
Reactチュートリアルを読む
-
このQiitaエントリがすごく参考になる
コンポーネント作成
以下、React.Component
ベースでなくReact.createClass
でコンポーネントを作る前提。
- 置き換えたいDOM構造の現状を把握する
-
せっかくリファクタするのであれば必要十分な数のDOMになるのが理想
-
ここではとりあえず置き換えるのをゴールとして、既存のHTMLのコピペでJSXを構成する
-
- コンポーネント作る
rails g react:component
- テンプレートで既にpartial使っているなら、感覚としては似ている。淡々とpartial単位でおきかえていくイメージ
- 一思いにブラウザでouter htmlをコピー、beautify https://ctrlq.org/beautifier/ とか
-
render
にhtmlをペーストする -
class
となっているところをclassName
にもれなく置き換える - HTML内にstyleが記述されているところはひとまずinner styleに置き換える
- 一応、もとのslimなどと見比べて自分を納得させる
コンポーネント修正
-
componentDidMount``getInitialState
などだいたい必要になるので書く - レンダリング内容取得のAPIが必要になればそれも書く
- スクロール位置などのDOM属性を参照したくなったら
ref
を使う - 必要に応じてJSのcallbackやclosureなどをうまく使って構造化する
ハマりどころ
- 出現する条件がレアなDOMを見落として移行から漏れる
- これを防ぐのがReact化の目的の一つだが、できればこのタイミングでもなんとかしたい
-
setState
は即座にstateを変更してくれるわけではない- 直前の
setState
でstateが変更された前提でstateへの参照をしているとハマる -
this.state
を参照する処理は最小限にするか、renderに押し込む?
- 直前の
- 何も考えずにReactコンポーネントとしてラップするとDOM階層が一つ増える
- 子セレクタに依存するスタイル命名規則をつかっているとCSS/JSの挙動が変わる
- たとえば、RSCSSを使わない
- もしくは階層に関する命名制約を外す
-
map
などで同じ要素を複数並べるときはkey
で一意性を保つようにする- 一意性が保たれていない場合、二つ目以降の要素が描画されない
-
render
内でreturn
したくなったときはreturn null
する
効果
- 人力DOM操作によるバグが減る
- データ取得の処理がコンポーネント自身にカプセル化される
- アプリのどこでコンポーネントを呼び出してもだいたい正しく動く
- 共起するあらゆるDOMを同一コンポーネントのJSXでほぼ説明できる
宿題
- 荒療治でも案外素直に動くので、やり残していることがいっぱい
- 無駄なバリデーション
- prop typesを使いたい人生
- mixin
- たとえばrenderだけがちがう複数のコンポーネントをどう綺麗に実装すべきか
- overrideしたい
- デザインパターンをうまく使いたい
- redux
- スタンダードになりつつあるようだが理解が追いついていないので保留
- どのような問題が解決されるのか
- どのような依存が発生するのか
- テスティング?
-
React.createClass
からReact.Component
ベースへの書き換え
葛藤
- DOM操作の複雑さがstate操作の複雑さへ保留される
- とはいえ、単なるデータ構造の問題へ縮小している
- DOM構造やinline styleをJS内に書くことになる
- 今まで守ってきたHTML/CSS/JSの分離とはなんだったのか
- サーバー > クライアント、クライアント > サーバのデータの流れが同じところで行われ、依然見通しが悪い
- comfort zoneからの巣立ち
- CoffeeScriptで書きたかった
- Slimで書きたかった
解決済の問題が多そう・Reactの恩恵を100%受けていなさそうなので、引き続き勉強する