見出し画像

【DOMを超越せよ】React Portal でモーダルやツールチップを「親の束縛」から解放し、配置を自由にする技術

97日目に「ヘッドレスUI」で機能の抽象化を学びました。本日98日目は、Reactにおける「DOMの階層構造」という制限を突破する最強のギミック、「React Portals」 を学習します。

モーダルやツールチップを作っていて、「親要素の overflow: hidden で表示が切れる!」「z-index が効かなくて困る!」と頭を抱えたことはありませんか?その苦しみ、Portalsがすべて解決します!


はじめに:なぜ「DOMツリー」はときに邪魔なのか?

Reactのコンポーネントは、コードに書いた通りにDOMツリー上に親子関係を作ります。しかし、UIの世界では「コード上の論理階層」と「視覚的な配置」が一致しないことがあります。

例えば、深いネストの中にいるコンポーネントから「画面全体を覆うモーダル」を出すとき、親のCSS(overflow: hidden や opacity)の影響をダイレクトに受けてしまうと、モーダルがズレたり隠れたりします。

React Portals は、コンポーネントの書き場所は維持したまま、実際のDOM要素を <body> 直下など、任意の場所にレンダリングさせるための機能です。


💡 1. Portals の仕組み

ReactDOM.createPortal(child, container) を使います。

第1引数にレンダリングしたい内容(JSX)、第2引数に配置したいDOMノードを指定します。

import ReactDOM from 'react-dom';

const Modal = ({ children }) => {
  // 💡 body 直下の #modal-root という div に飛ばす
  return ReactDOM.createPortal(
    <div className="modal-overlay">{children}</div>,
    document.getElementById('modal-root') // 事前にindex.htmlに作っておく
  );
};

💡 2. なぜ「イベント伝播」は維持されるのか?

Portals のすごいところは、物理的に離れた場所にレンダリングされているのに、Reactのイベントシステム上は「親コンポーネントの直下」にいると見なされる点です。

つまり、Portalで飛ばしたモーダル内のボタンを押したとき、そのモーダルの親(Portalを使っているコンポーネント)で発生したイベントハンドラーは正常に呼び出されます。物理的なDOM階層を超えて、論理的なReact階層が維持されているからです。


💡 3. こんな時に Portals を使う

  1. モーダル/ダイアログ: 画面全体を覆う必要があるため。

  2. ツールチップ/ポップオーバー: 親の overflow: hidden から抜け出し、画面端まで表示させたいとき。

  3. ドラッグ&ドロップ: 要素をリストから切り離してマウスに追従させるとき。


📝 98日目のまとめと課題

✅ 本日の習得ポイント

  • DOM階層の脱出: createPortal で、親のCSS制約を受けないレンダリングを行う。

  • イベントシステムの継承: 物理的に離れていても、論理的な親子関係をReactが維持してくれる。

  • 配置の自由化: デザイン上の「重なり」や「はみ出し」問題を、設計レベルで解決する。

📝 98日目の課題

  1. index.html に <div id="modal-root"></div> を追加し、モーダルコンポーネントを Portal を使ってそこに飛ばしてみましょう。

  2. Portal の中にあるボタンから、その親のコンポーネント(Portalを呼んでいる親)のステートを更新できるかテストし、「論理階層が繋がっていること」を体験してみてください。


もっとReactを学びたい人はここから!

#駆け出しエンジニアと繋がりたい #プログラミング学習 #React #100DaysOfCode #今日の積み上げ

いいなと思ったら応援しよう!