Edited at

React移行メモ

More than 3 years have passed since last update.

Rails4アプリのjQueryフロントエンドをReact.jsに置き換えるリファクタ活動を始めたので、これまでの理解をメモ


コードの臭い


  • (jQueryによる)DOM操作・クラス操作の累積

  • addしてremoveしてaddしてhide...本当に意図通り動いているのか、何か忘れてないか

  • 特殊なDOM構成の存在を前提とした操作

  • DOMがどこから操作されているのか不明


移行指針


  • 問題の変換


    • 状態変化に伴うDOM操作の処理を自分で計算せず、すべてrenderに押し付ける

    • HTML差分計算・再描画に関する問題・思考を最小化し、stateの操作に集中することでビジネスロジックの整合性を確認する



  • 双方向の思考を一応念頭に置く


    • ボトムアップ、コンポーネント指向(?)


      • 小さいコンポーネントを先に置き換えて、大きいコンポーネントでそれを使う



    • トップダウン、インターフェース指向(?)


      • 大きいコンポーネントを先に置き換えて、途中で出てきた再利用性の高い部分をどんどん別コンポーネントに分割する





  • ついでにいろいろできそうだが、最小の工数で移行する


入り口


  • どこから導入すべきか

  • 修正の必要が発生したが、DOM操作がすでに溢れ、次の修正でバグが入りそうな箇所


    • DOM操作を追加・修正せざるを得ない箇所

    • API経由の遅延描画に置き換えたい箇所



  • 壊れていないものを直そうとしない


    • もし静的解析で定量化できたら、複雑な箇所から置き換える




準備


コンポーネント作成

以下、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などと見比べて自分を納得させる



コンポーネント修正



  • componentDidMountgetInitialStateなどだいたい必要になるので書く

  • レンダリング内容取得の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%受けていなさそうなので、引き続き勉強する