Portal
Portal
Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的方式。
ReactDOM.createPortal(child, container)
第一个参数是任何可渲染的 React 元素,第二个参数是一个 DOM 元素。
来看一个场景,如下:
import { useState } from "react";
function App() {
const [isShow, setIsShow] = useState(false);
return (
<div>
<h1>App</h1>
<button onClick={()=>setIsShow(!isShow)}>Show/Hide</button>
{isShow ? <Modal /> : null}
</div>
);
}
function Modal() {
return (
<div style={{
width : "450px",
height : "250px",
border : "1px solid",
position : "absolute",
left : "calc(50% - 225px)",
top : "calc(50% - 125px)",
textAlign : "center",
lineHeight : "250px"
}}>Modal</div>
)
}
export default App;
在上面的示例中,Modal 是一个模态框,App 根组件中能够控制该模态框组件是否显示。功能来讲并没有什么问题,但是从最终渲染出来的 html 结构上来讲,将整个模态框都放在根这个 div 中不是那么合适,生成的 html 结构上,这个模态框能够渲染到 Modal 那个 div 里面。
渲染后的结构如下:
<body>
<div id="root">
<div>
<h1>App</h1>
<button>Show/Hide</button>
<div style="width: 450px; height: 250px; border: 1px solid; position: absolute;
top: calc(50% - 125px); text-align: center; line-height: 250px;">
Modal
</div>
</div>
</div>
</body>
这样会产生一些问题,比如父组件设置了额外的样式可能会影响子组件。
这种情况下,可以使用 Portal 来解决这个问题。
使用
Portal 的使用方式非常简单,只需要使用 createPortal 方法来指定渲染到哪个元素中即可。Portal 是和 React 渲染相关的,所以 createPortal 方法来自于 react-dom。
import {createPortal} from 'react-dom';
function Modal() {
return createPortal((<div style={{
width : "450px",
height : "250px",
border : "1px solid",
position : "absolute",
left : "calc(50% - 225px)",
top : "calc(50% - 125px)",
textAlign : "center",
lineHeight : "250px"
}}>Modal</div>),document.getElementById("modal"))
}
export default Modal;
渲染后的结构如下:
<body>
<div id="root">
<div>
<h1>App</h1>
<button>Show/Hide</button>
</div>
</div>
<div id="modal">
<div style="width: 450px; height: 250px; border: 1px solid; position: absolute;
top: calc(50% - 125px); text-align: center; line-height: 250px;">
Modal
</div>
</div>
</body>
Portal 的典型用例是当父组件有 overflow: hidden 或 z-index 样式时,但需要子组件能够在视觉上跳出其容器。例如,对话框、悬浮卡以及提示框。
事件冒泡
以上面的例子为例,看上去模态框已经渲染到了 modal 这个元素里面,但是在 React 中事件冒泡是按照组件结构来进行冒泡的,在组件树中模态框组件仍然是在根组件中。
尽管 Portal 可以被放置在 DOM 树中的任何地方,但在任何其他方面,其行为和普通的 React 子节点行为一致。由于 portal 仍存在于 React 树, 且与 DOM 树中的位置无关,那么无论其子节点是否是 Portal,像 context 这样的功能特性都是不变的。
一个从 Portal 内部触发的事件会一直冒泡至包含 React 树的祖先,即便这些元素并不是 DOM 树中的祖先。