文章目录
适合场景
React Portal 是 React 提供的一种机制,让你可以将子组件渲染到 DOM 的指定节点中,而不是默认的父组件 DOM 层级中。
非常适合:
✅ 弹窗(Modal)
✅ Tooltip
✅ Popover
✅ 全局消息提示(Toast)等浮层组件的实现
基本语法
import { createPortal } from 'react-dom';
const MyPortal = () => {
return createPortal(
<div>I’m rendered in a portal!</div>,
document.getElementById('portal-root')! // 目标容器
);
};
你需要确保 HTML 里有这个 DOM 节点:
<!-- index.html -->
<body>
<div id="root"></div>
<div id="portal-root"></div>
</body>
示例:Modal 弹窗
1. 创建一个简单的 Modal.tsx
import React from 'react';
import ReactDOM from 'react-dom';
type ModalProps = {
visible: boolean;
onClose: () => void;
children: React.ReactNode;
};
const Modal: React.FC<ModalProps> = ({ visible, onClose, children }) => {
if (!visible) return null;
return ReactDOM.createPortal(
<div style={styles.mask}>
<div style={styles.modal}>
{children}
<button onClick={onClose}>Close</button>
</div>
</div>,
document.getElementById('portal-root')!
);
};
const styles = {
mask: {
position: 'fixed' as const,
top: 0, left: 0, right: 0, bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.3)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
modal: {
background: '#fff',
padding: 20,
borderRadius: 8,
minWidth: 300,
},
};
export default Modal;
2. 在 App 中使用
import React, { useState } from 'react';
import Modal from './Modal';
const App = () => {
const [visible, setVisible] = useState(false);
return (
<>
<button onClick={() => setVisible(true)}>Open Modal</button>
<Modal visible={visible} onClose={() => setVisible(false)}>
<h2>This is a Portal Modal</h2>
</Modal>
</>
);
};
export default App;
为什么要用 Portal?
如果你直接把 Modal 放在组件里,它可能被某些 CSS 样式限制(比如 overflow: hidden
、z-index
),无法正确展示。
Portal 允许你将组件“挂”在 HTML 的任意节点(如根节点外层),避免被父组件样式影响,同时又保持了 React 的事件机制和数据流。
TypeScript 中 Portal 类型定义?
ReactDOM.createPortal
的类型如下:
createPortal(
children: ReactNode,
container: Element | DocumentFragment
): ReactPortal
所以你通常会这么写:
ReactDOM.createPortal(
<div>内容</div>,
document.getElementById('portal-root')!
);
其中返回值类型是 React.ReactPortal
,它是 ReactNode
的子类型,意味着它可以被正常地渲染。