有些 UI 天生应该盖在所有内容之上:弹窗、抽屉、全局提示。但它们常被渲染在深层嵌套的组件里,父容器一旦设了 overflow: hidden、z-index 或 transform,样式就被压住。createPortal 把渲染出口换到 document.body 或专用节点,事件冒泡却仍按 React 组件树走,逻辑完全不受影响。
用法是 createPortal(children, container):children 还是普通 React 元素,container 是真实 DOM 节点。注意它只改挂载位置,不改 React 树关系——组件上下文、事件冒泡都保持原样,因此父组件依旧能捕获到 Portal 内组件冒泡上来的合成事件。
import { createPortal, useState } from 'react'; import { createRoot } from 'react-dom/client'; function Modal({ open, onClose }) { if (!open) return null; return createPortal( <div className="mask" onClick={onClose}> <div className="dialog">点遮罩关闭,事件仍冒泡到父组件</div> </div>, document.body // 渲染出口换到 body ); } export default function App() { const [open, setOpen] = useState(false); return ( <div style={{ overflow: 'hidden' }}> <button onClick={() => setOpen(true)}>打开</button> <Modal open={open} onClose={() => setOpen(false)} /> </div> ); }