CSSレスポンシブデザインをSPAで使うと破滅する
あくまで見た目の調整、表示・非表示のコントロールなので
下手に使うと、デバイス毎にインタラクションが違うUIのstateが無茶苦茶複雑になっていく
画面サイズ毎にCSSで手軽に見た目だけを切り替える、というのなら良い
しかし、画面サイズ毎にインタラクションが違う部品が出てくると設計が破綻する
画面サイズ毎の見た目だけでなく、イベントハンドラ、stateがごちゃ混ぜになる
コードが追えなくなり、バグの温床になる
では、どうするか?
素直に画面サイズ毎、デバイス毎、あるいはインタラクション毎のReactコンポーネントを書けばいい
使いまわせる部品は、コンポーネントとして切り出して再利用する
歴史を紐解く
当時のwebにはコンポーネントが存在しなかった
単体で複雑な状態遷移をするインタラクティブなパーツが無かった
せいぜいカレンダーウィジェットぐらい
HTML+JSのパーツを使いまわすしくみが無かった
そこでレスポンシブデザインが編み出された
1つの複雑なファイルを作り、画面サイズに応じて部品を出したり消したり、CSSを切り替えたりする
少ない手間で色々な画面サイズに対応できるようになった
2015年ごろ?
iPad以外にも、AndroidやKindle等たくさんの10 inch tabletが普及した
レスポンシブデザインが対象としていた、画面サイズ毎のUI切り替えでは対応しきれない
単純なレスポンシブデザインでは、マウス用のUIが10インチタブレットに表示されてしまう
雑にモバイルと言うと、人によって違う意味を指していて混乱するようになってきた 2018年
SPAでは、見た目よりもインタラクションのコーディングの比重が増えた 画面サイズ毎に、見た目だけでなくインタラクションも違う
つまり同じ部品でも、デバイス毎に取りうるstateもイベントハンドリングも全然違ったりする
stateを持った複雑なUIに、CSSレスポンシブデザインを混ぜると収拾がつかなくなる
特定の画面サイズだけで表示されるパーツ
特定の画面サイズでしか参照しないstate
特定の画面サイズでしか呼び出されない・登録されないイベントハンドラ
バグがあった時、何がどこまで影響しているのか追えない
関係ないコードがどこなのかわからない
本当にこのstate・イベントハンドラ・パーツは関係していないのか?
全然わからない。関係していない事を証明するのは難しい。
CSSで非表示にしているだけで、DOMには存在しているから
非表示のReactコンポーネントの中でしっかりライフサイクルが走っていて、イベントハンドラは登録され、stateを持って動いている
scrapboxのnavbarとsearchformまわりがそうだった
「mobile用のsearch formを表示するべきかどうか」というstateができてくる
https://gyazo.com/ea72cb71236d0d77d02150292feca87e
このstateは、mobile以外の画面には関係ない
mobileの場合とそれ以外でnavbarコンポーネントを切り替えていれば
mobile navbarでは「search formを表示するかどうか」というシンプルなstateになる
desktop navbarでは、そのstateはそもそも不要になる
脳の負荷が高すぎる
2人の人に同時に話しかけられている感じ
機能追加した時の動作確認の手間が2倍になる
例えばモバイル用の機能を追加しただけでも、デスクトップでも動作確認しなければならない
同じコンポーネントにごちゃ混ぜに書いているから
最初から完全に別のコンポーネントとして分割されていれば、動作確認は1回で済む
しかも、デスクトップでは「以前と変わっていない事」を確認しなければならない
とても難しい
pull requestの本筋とは関係ない部品の、以前の動作を知っている人でなければレビューできなくなったりする
どうやるか?
CSSでレスポンシブデザインするのやめる
見た目だけの切り替えなら、とりあえずはレスポンシブでいい
デバイス毎のstateやイベントハンドラが現れたら切り替える
素直にターゲットデバイス毎に別々のコンポーネントを書く
デバイスに応じて適切なコンポーネントを切り替えるコンポーネント、でwrapする
デバイスに応じて変えたりする
画面サイズ
タッチパネルかポインティングデバイスか
コンポーネント内で共通して使うパーツは、さらに個別のコンポーネントとして切り出す
2つにわけて書けばシンプルになる
1つの超複雑なコンポーネントを作る必要は無い
例
テロメアは、mobile/desktopで別々のコンポーネントに分けた 超読みやすくなった
daiiz.iconがやってくれた
Linesコンポーネント
見た目とインタラクションを分解した
touch device・pointing deviceで共通の見た目のエディタと
touch device・pointing deviceそれぞれのイベントハンドリングに分解した
こういう感じ
<MobileTouchEvent><Lines /></MobileTouchEvent>
<DesktopTouchEvent><Lines /></DesktopTouchEvent>
子のイベントを全部親に投げてる
これもdaiiz.iconがやってくれた
上は2018年ごろの考え
最初からデバイス毎にコンポーネントをわけるのはやめた
レスポンシブデザインと併用している