概要: このチュートリアルでは、React ポータルを使用して、親コンポーネントの DOM 階層の外にある DOM ノードに子要素をレンダリングする方法を学習します。
React ポータルの紹介
React ポータルは、親コンポーネントの DOM 階層の外にあるDOMノードに子要素をレンダリングする方法を提供します。
React ポータルは、モーダル、ツールチップ、ドロップダウンなど、通常の DOM 階層の外側にコンポーネントをレンダリングする場合に役立ちます。
ポータルを作成するには、ReactDOM.createPortal()
関数を使用します。createPortal
関数は2つの引数を取ります。
- レンダリングする JSX コンテンツ。
- JSX コンテンツをレンダリングする DOM ノード。
React ポータルを実演するために、React で再利用可能なモーダルコンポーネントを作成します。
新しい React プロジェクトの作成
まず、ターミナルを開き、次のコマンドを実行して新しい React プロジェクトを作成します。
npx create-react-app react-portals
Code language: JavaScript (javascript)
次に、react-portals
ディレクトリに移動します。
cd react-portals
Code language: JavaScript (javascript)
次に、次のコマンドを実行して React アプリを起動します。
npm start
Code language: JavaScript (javascript)
src
ディレクトリ内のすべてのファイルを削除し、最初から始めます。
モーダルの最初のバージョンを作成する
ステップ1. 画面にApp
コンポーネントを表示するindex.js
ファイルを作成します。
import ReactDOM from 'react-dom/client';
import App from './App';
const el = document.querySelector('#root');
const root = ReactDOM.createRoot(el);
root.render(<App />);
Code language: JavaScript (javascript)
ステップ2. App
コンポーネントを作成します。
import { useState } from 'react';
const App = () => {
return (
<div className="container">
Modal
</div>
);
};
export default App;
Code language: JavaScript (javascript)
ステップ3. Modal
コンポーネントを作成します。
import './Modal.css';
const Modal = () => {
return (
<div className="modal-container">
Modal
</div>
);
};
export default Modal;
Code language: JavaScript (javascript)
ステップ4. 空のModal.css
ファイルを作成します。
ステップ5. モーダルを表示するようにApp
コンポーネントを拡張します。
import { useState } from 'react';
import Modal from './Modal';
const App = () => {
const [showModal, setShowModal] = useState(false);
const handleClick = () => setShowModal(!showModal);
return (
<div className="container">
<button type="button" onClick={handleClick}>
Open
</button>
{showModal && <Modal />}
</div>
);
};
export default App;
Code language: JavaScript (javascript)
App
コンポーネントは、showModal
状態を使用してモーダルを表示/非表示にします。showModal
がtrueの場合、Modal
コンポーネントが表示され、false
の場合、Modal
コンポーネントは表示されません。
App
コンポーネントには、クリックするとモーダルを開くボタンもあります。クリックイベントが発生すると、モーダルを表示するために、showModal
状態の値をfalse
からtrue
に切り替えます。
ステップ6. モーダルにコンテンツを表示します。
import './Modal.css';
const Modal = () => {
return (
<div className="modal-container">
<div className="modal">
<h2>Modal is cool </h2>
<p>This is a modal in React</p>
</div>
</div>
);
};
export default Modal;
Code language: JavaScript (javascript)
ステップ7. Modal.css
ファイルにスタイルを追加します。
.modal-container {
/* create an overlay */
position: absolute;
inset: 0;
background-color: rgba(0, 0, 0, 0.5);
/* center the modal */
display: flex;
justify-content: center;
align-items: center;
}
.modal {
background-color: white;
padding: 1rem;
border-radius: 0.5rem;
}
Code language: JavaScript (javascript)
ボタンをクリックすると、モーダルが表示されます。

これは、.modal-container
要素でabsolute
位置を使用し、すべての親要素がstatic
以外の位置を持っていないため機能します。
.container
要素のposition
がModal.css
ファイルでrelative
に設定されていると、モーダルは正しく機能しません。
.container {
position: relative;
}
/* other rules */
Code language: JavaScript (javascript)

この問題を解決するには、.container
要素ではなくbody
要素の下にモーダルをレンダリングする必要があります。そのためには、React ポータルを使用します。
React ポータルの使用
ステップ1. public/index.html
ファイルのbody
要素の直下に、クラス.model-wrapper
を持つdivを追加します。
...
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div class="modal-wrapper"></div>
</body>
...
Code language: JavaScript (javascript)
ステップ2. React ポータルを使用してモーダルをレンダリングします。
.modal-wrapper
要素内にモーダルコンポーネントをレンダリングするには、ReactDOM.createPortal()
関数を使用します。
import ReactDOM from 'react-dom';
import './Modal.css';
const Modal = () => {
return ReactDOM.createPortal(
<div className="modal-container">
<div className="modal">
<h2>Modal is cool </h2>
<p>This is a modal in React</p>
</div>
</div>,
document.querySelector('.modal-wrapper')
);
};
export default Modal;
Code language: JavaScript (javascript)
開くボタンをクリックすると、モーダルが正しく表示されます。
これまでのところ、モーダルには静的なコンテンツしかありません。通常、モーダルには親コンポーネントによって提供される動的なコンテンツが含まれており、それ自体を閉じるボタンがあります。これらの機能をModal
コンポーネントに追加するには、propsを使用できます。
ステップ3. App
コンポーネントを拡張します。
import { useState } from 'react';
import Modal from './Modal';
const App = () => {
const [showModal, setShowModal] = useState(false);
const handleClick = () => setShowModal(!showModal);
const handleClose = () => setShowModal(false);
const actions = (
<button type="button" onClick={handleClose}>
Close
</button>
);
const modal = (
<Modal onClose={handleClose} actions={actions}>
<p>This is a modal!</p>
</Modal>
);
return (
<div className="container">
<button type="button" onClick={handleClick}>
Open
</button>
{showModal && modal}
</div>
);
};
export default App;
Code language: JavaScript (javascript)
仕組み
まず、onClose
とactions
プロップをModal
コンポーネントに追加します。
<Modal onClose={handleClose} actions={actions}>
Code language: JavaScript (javascript)
次に、開始タグと終了タグの間に配置することで、子コンポーネントをModal
コンポーネントに提供します。
<Modal onClose={handleClose} actions={actions}>
<p>This is a modal!</p>
</Modal>
Code language: JavaScript (javascript)
<Modal>
タグと</Modal>
タグの間に任意のコンテンツを配置できることに注意してください。Modal
コンポーネントでは、それらはchildren
プロップに格納されます。
次に、閉じるボタンを含むモーダルのactions
を定義し、handleClose
イベントハンドラをクリックイベントに登録します。
const actions = (
<button type="button" onClick={handleClose}>
Close
</button>
);
Code language: JavaScript (javascript)
最後に、handleClose
関数でshowModal
状態をfalse
に設定してモーダルを閉じます。
const handleClose = () => setShowModal(false);
Code language: JavaScript (javascript)
ステップ4. Modal
コンポーネントにpropsを追加します。
onClose
、children
、actions
プロップをModal
コンポーネントに追加します。
import ReactDOM from 'react-dom';
import './Modal.css';
const Modal = ({ onClose, children, actions }) => {
return ReactDOM.createPortal(
<div className="modal-container" onClick={onClose}>
<div className="modal">
{children}
{actions}
</div>
</div>,
document.querySelector('.modal-wrapper')
);
};
export default Modal;
Code language: JavaScript (javascript)
Modal
コンポーネント内では
- オーバーレイがクリックされたときにモーダルを閉じるために、
.modal-container
要素のクリックイベントにonClose
関数を登録します。 - childrenとactionsプロップをレンダリングします。
「開く」ボタンをクリックすると、モーダルが表示されます。「閉じる」ボタンまたはオーバーレイをクリックすると、モーダルが閉じます。
しかし、モーダルの内部をクリックすると、これも閉じます。これは予期しない動作です。その理由は、クリックイベントが.modal-container
の子要素から.modal-container
要素に伝播されるためです。
子要素から親要素へのイベントの伝播を停止するには、イベントオブジェクトのstopPropagation()
メソッドを使用できます。
import ReactDOM from 'react-dom';
import './Modal.css';
const Modal = ({ onClose, children, actions }) => {
const handleClick = (e) => e.stopPropagation();
return ReactDOM.createPortal(
<div className="modal-container" onClick={onClose}>
<div className="modal" onClick={handleClick}>
{children}
{actions}
</div>
</div>,
document.querySelector('.modal-wrapper')
);
};
export default Modal;
Code language: JavaScript (javascript)
仕組み
まず、.modal
要素にonClick
プロップを追加します。
<div className="modal" onClick={handleClick}>
Code language: JavaScript (javascript)
次に、handeClick
関数でイベントオブジェクトのstopPropagation()
メソッドを呼び出すことによって、イベントの伝播を停止します。
const handleClick = (e) => e.stopPropagation();
Code language: JavaScript (javascript)
これで、「閉じる」ボタンを除いて、モーダルの内部をクリックしてもモーダルは閉じなくなります。
スクロールの問題の修正
Appコンポーネントに長いコンテンツがあり、スクロールすると、オーバーレイが画面全体を覆いません。

これを修正するには、.modal-container
のposition
をabsolute
からfixed
に変更できます。
.modal-container {
/* create an overlay */
position: absolute;
inset: 0;
background-color: rgba(0, 0, 0, 0.5);
/* center the modal */
display: flex;
justify-content: center;
align-items: center;
}
Code language: CSS (css)
React モーダルプロジェクトのソースコードのダウンロード
React モーダルプロジェクトのソースコードをダウンロードしてください。
まとめ
- React ポータルを使用して、親コンポーネントの DOM 階層の外にある DOM ノードに子要素をレンダリングします。