摘要:在本教程中,您将学习如何使用 React 传送门将子元素渲染到父组件 DOM 层级结构之外的 DOM 节点中。
React 传送门介绍
React 传送门提供了一种将子元素渲染到父组件 DOM 层级结构之外的 DOM 节点中的方法。
在渲染组件到常规 DOM 层级结构之外时,例如模态框、工具提示或下拉菜单,React 传送门很有用。
要创建传送门,可以使用 ReactDOM.createPortal()
函数。createPortal
函数接受两个参数
- 要渲染的 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. 创建一个 index.js
文件,该文件在屏幕上显示 App
组件
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
元素在 Modal.css
文件中将 position
设置为 relative
,那么模态框将无法正常工作。
.container {
position: relative;
}
/* other rules */
Code language: JavaScript (javascript)

要解决此问题,我们需要在 body
元素而不是 .container
元素下渲染模态框。为此,我们将使用 React 传送门。
使用 React 传送门
步骤 1. 在 public/index.html
文件中,将一个具有类 .model-wrapper
的 div 直接添加到 body
元素下
...
<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 传送门渲染模态框。
使用 ReactDOM.createPortal()
函数在 .modal-wrapper
元素中渲染模态框组件
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
组件中,我们可以使用 属性。
步骤 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
组件。
将 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
组件中
- 将
onClose
函数注册到.modal-container
元素的单击事件,以便在单击叠加层时关闭模态框。 - 渲染 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)
工作原理。
首先,将 onClick
属性添加到 .modal
元素
<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 传送门将子元素渲染到父组件 DOM 层级结构之外的 DOM 节点中。