定義
「一箇所だけ修正したのに画面全体が描き直される」という React の性能問題を、目ではなく 自動テストで監視 したいときに使う技法だ。特定のコンポーネントが何回描き直されたかを数字で数え、編集後も無関係な部分が0回だけ描かれたかを検査する。
用語を解くと:再レンダー(re-render) は React がコンポーネント関数を再実行して画面を更新すること、隔離(isolation) はある部分を変えても兄弟・祖先コンポーネントが不要に描き直されない状態を言う。jest.mock はテストで特定のモジュールを偽版に差し替える Jest 機能、baseline スナップショットは「正常なときの基準値」を前もって撮っておいたものだ。
正確に言えば React レンダー回数の隔離テスト は、この再レンダー隔離を 単体テストとして契約化(コードが守るべき約束として固定) する方法だ。対象コンポーネントを jest.mock で包んで id ごとのレンダー回数を外部の Map に記録し、値を変える 直前 の baseline スナップショットと以後を比較する。フォーカス・マウントのような付随的な再レンダーは、スナップショットを撮る時点を調節して測定から分離する。
文脈の中で理解する
「深いブロックだけ編集したのに、画面の遠くにある兄弟も描き直される」という不満は、目では見えにくい。プロファイラは CI に入れにくく、「遅くなった」という体感だけでは回帰を捕まえられない。この方法は リファクタの前に 「遠い枝は追加レンダー0回」をテストで刻んでおき、memo・read-model・store 分離を入れた後に 同じテストが GREEN かどうかで改善を数字で証明する。
なぜ必要か
React.memo、selector、external store の最適化はリファクタ一回で崩れやすい。プロファイラは CI に入れにくく、E2E は遅く flake しやすい。「深いノードだけ修正したとき遠い兄弟 subtree は0回追加レンダー」のような 構造的不変 をテストで固定すれば、O(depth)→O(1) のような回帰を素早く捕まえる。
動作原理
- ギャップ分析:既存テストが flat vs nested、隔離 vs depth だけ、などどの軸をカバーしているかを表にする。共通集合(例:再帰+値変更の隔離) だけを新たに追加する。
- Mock プローブ:
jest.mock('./RowChrome', () => ({ RowChrome: (props) => { counts.set(props.id, (counts.get(props.id) ?? 0) + 1); return ActualRowChrome(props); } }))— 原本は実行、カウントだけ増やす。 - import 順序:mock はホイスティングされるので、テスト対象の import は mock の 下 に(
eslint-disable import/firstパターン)。 - シナリオ:ツリーをレンダー → (必要時)focus で editor をスワップ → baseline スナップショット → 決定的な値変更を1回(Enter split など)→ far branch
expect(count).toBe(baseline)。 - 回帰:アーキテクチャ改善の後、同じテストで祖先も baseline 維持(O(1) の証明)を要求できる。
実務での適用
- ツリー・リスト・テーブルの行単位の memo・store 購読を設計する前後で、far branch が0増加であることを assert する。
- typing シミュレーションより キーボードアクション一回(split、toggle)の方が jsdom で決定的だ。
- Provider を mock して コンテナ render === 0 を測定するときは、コンテナと1:1でレンダーされる下位(Provider)をプローブする。
- テスト名に「隔離」「O(1)」の意図を明示して、後でスキップされないようにする。
トレードオフ
- mock は実装の細部に結合する — プローブ対象コンポーネントの名前が変わればテストも修正。
- グローバルな
Mapはテスト間でbeforeEach初期化が必要。 - 「0追加レンダー」は そのシナリオ にだけ該当 — 別の interaction 経路は別途テスト。
- React Strict Mode の double-render は setup で一度織り込むか、プローブ設計を調整する。
使ってはいけない場合
- スナップショットの全 DOM 比較だけで性能契約を代替しようとするとき — 意図が不明確だ。
- プローブが多すぎて、すべてのラッパーを mock するとテストが実装のミラーになるとき。
よくある間違い
- baseline を focus の 前 に撮り、static→editor のスワップを値変更と誤認する。
- mock が原本を呼ばず、動作は壊れているのにカウントだけ通過する。
- flat な3兄弟テストだけがあり、深さ+値変更 の軸を空けたまま。
userEvent.typeの長文入力を flake なく隔離テストに使う。
関連概念
- react-context-render-granularity — context・memo が壊れる文脈
- normalized-render-entry-projection — 隔離を作る read-model パターン
- jest-esm-vendor-shim — Jest 環境での mock・import の問題