定義
サーバーでレンダリングした画面でクリック・送信といったインタラクションが 複数同時に 効かなくなったら、それはたいていバグが複数あるのではなく、クライアント JavaScript が実行できず ハイドレーションが丸ごと不発 になった一つの根本原因である。
ハイドレーション(hydration): サーバーが作った静的 HTML に、ブラウザで JS があとからイベントリスナ・状態を付けて「生きて動く」ようにする過程。これが終わる前はボタンはただの絵にすぎない。バンドルがパース/実行の段階で死ぬと、ハイドレーション自体が開始・完了できない。
なぜ必要か
バグ報告は「トグルが効かない」「ログインボタンが効かない」「送信が効かない」のように 複数の症状 で届く。これをバグ 3 個と見てそれぞれ掘り下げると時間を無駄にする。サーバーレンダリング(SSR)アプリでこれらの症状の共通点は、全部がイベントハンドラだということだ。サーバーが送った HTML は問題なく見えるが、その上にイベントを付けるクライアント JS が死ぬと、画面の すべての インタラクションが一度に消える。画面は正常に見えるので、さらに紛らわしい。
動作原理
バンドルの実行が死ぬ二つの筋道:
- パース失敗: 旧ブラウザが新しい構文を読めず
SyntaxErrorでファイル全体が中断する。最初のエラー以降、一行も実行されない。 - ランタイム失敗: 存在しない API を呼んで
TypeErrorが起き、ハイドレーションの途中で中断する。そのコードが初期実行パスにあると画面全体のインタラクションが死ぬ。
これは ハイドレーション不一致(サーバー・クライアントのマークアップが違って React がツリーを捨てる別の事例)とは違う。不一致は「実行はされるが結果がずれる」であり、ここで言うのは「実行そのものができない」である。
実務での適用
- 複数の症状を「バグ N 個」ではなく「一つの実行失敗」として先に仮説を立てる。
- コンソールの最初のエラーの種類で切り分ける:
SyntaxError→ 構文(トランスパイル)、TypeError: is not a function→ 存在しない API(ポリフィル)。 - 原因のレイヤーを直す(構文はダウンレベル、存在しない API はポリフィル)。
- 特定のブラウザ/バージョンでのみ再現するなら、互換性(構文・API)が原因である確率が高い。
トレードオフ
| 診断アプローチ | 利点 | コスト |
|---|---|---|
| 症状ごとの個別デバッグ | すぐ着手できる | 共通原因を見逃すと時間を無駄にする |
| 共通根本原因の仮説を優先 | 一度で解決 | 初期仮説が外れると回り道が必要 |
使うべきでない場合
- たった一つの機能だけが効かない場合 — それは本当に個別のバグでありうるので、共通原因の仮説を強要しない。
- サーバー/クライアントのマークアップ不一致警告が明確な場合 — これは不発ではなく不一致なので処方が違う。
よくある失敗
- 画面が見えるので「レンダーはできた」とハイドレーションを除外する。
- 症状ごとに別チケットを切って、同じ原因を三回調査する。
- コンソールの最初のエラーを見ずに UI だけをいじる。
関連概念
- syntax-transpilation-vs-runtime-polyfill — バンドルを殺す二つの原因(構文・API)
- ssr-hydration-mismatch — 実行はされるがサーバー/クライアントがずれる別の事例
- browser-compat-interaction-smoke — 死んだハンドラを実際に捕まえるテスト