Help us understand the problem. What is going on with this article?

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%受けていなさそうなので、引き続き勉強する

satzz
Web Engineer 2009-2011 Hatena(part-time) 2011-2015 DeNA
http://satzz.me
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away