こんにちは。Reactの世界へようこそ!
現場でバリバリとコードを書き散らしていると、どうしても「綺麗な設計」ばかりを追い求めてしまいがちですが、まずは「動く!楽しい!」という感覚を大切にしてほしいなと思います。
今日は、Reactを触り始めると誰もが一度はぶつかる「あれ?モーダルが変な場所に出ちゃった…」という悩みを、魔法のように解決してくれる『Portals(ポータル)』という機能についてお話ししますね。
—
Reactの「親子関係」という縛り
まず、Reactの基本ルールとして「コンポーネントは親の中に子がいる」という階層構造があります。これはお部屋の「入れ子」のようなものです。
例えば、`App` という広いリビングの中に `Header` や `Main` があり、その中にさらに `Modal` がある……といったイメージですね。
しかし、ここで一つ困ったことが起きます。
「モーダルだけは、リビングの隅っこじゃなくて、画面全体に覆いかぶさってほしい!」 という時です。
親コンポーネントが「狭い箱」の中に閉じ込められていると、その中のモーダルもその箱のサイズや境界線(CSSの `overflow: hidden` など)の影響を受けてしまい、画面全体を覆うのが非常に難しくなるんです。
Portalsという「どこでもドア」
そこで登場するのが Portals(ポータル) です。
これは、Reactのコンポーネントを「論理的には今の場所に置きつつ、DOM(画面上の実体)としては全く別の場所にワープさせる」という仕組みです。
例えるなら、「リビング(親)にいるんだけど、窓を開けて庭(別のDOM)に荷物を投げ込む」ような感覚です。Reactは「あの子は庭にいるけど、うちの家族(親コンポーネント)だよね」とちゃんと管理してくれます。
どうやって使うの?
Reactには `createPortal` という関数が用意されています。これを使えば、「どこに飛ばすか」を指定するだけでOKです。
まずは、HTMLファイル(`index.html`など)に、モーダルを出現させるための「受け皿」を作っておきましょう。
次に、React側でこう書きます。
import React from ‘react’;
import ReactDOM from ‘react-dom’;
const Modal = ({ children }) => {
// 1. 飛び先のDOM要素を取得します
const modalRoot = document.getElementById(‘modal-root’);
// 2. createPortalを使って、childrenをmodalRootの中にレンダリングします
return ReactDOM.createPortal(
,
modalRoot // ここにワープさせる!
);
};
export default Modal;
これだけで、モーダルは `App` コンポーネントの構造に縛られず、`#modal-root` という「画面の一番外側」にポンと飛び出してくれるんです。
—
なぜPortalsを使うと幸せになれるのか?
初心者の頃は「そのままでもCSSで無理やり位置調整すればいいんじゃない?」と思うかもしれません。でも、現場ではPortalsが必須級のテクニックとして愛されています。理由は主に2つです。
1. CSSの呪縛から解放される
親要素に `overflow: hidden` や `z-index` が設定されていると、モーダルが背景に隠れたり、一部が欠けたりして泣きを見ることがあります。Portalsを使えば、それら全てを無視して「一番手前」に表示できます。
2. イベントの伝播(バブリング)はそのまま
これが一番すごいところです。DOM上ではバラバラの場所にいても、Reactの仕組み上は「親子関係」が維持されています。だから、モーダル内で起きたクリックイベントを親コンポーネントで受け取る、といったReactらしい書き方がそのままできるんです。
最後に:つまずいたあなたへ
もし、初めてPortalsを書いてみて「あれ、表示されない!」と焦ったとしたら、大抵は飛び先の `id` が間違っているか、`index.html` にその要素を書き忘れているかのどちらかです。
「プログラムが動かないのは、君が悪いんじゃなくて、ただの設定ミスだよ」と、いつも若手には伝えています。焦らず、一つずつ確認していけば大丈夫です。
モーダル、ツールチップ、ドロップダウンメニュー。これらはユーザーがアプリを触る時に一番目にする重要なパーツです。Portalsを使いこなせれば、あなたの作るUIは一気にプロのクオリティに近づきますよ。
それでは、素敵なReactライフを!また何かあればいつでも聞いてくださいね。

コメント