React Portal - 弹出层的优秀解决方案
对于需要使用弹出层的需求 ,Portal可以说是提供了一种完美的解决方案。相比于React Native中的实现更多的使用Modal或者绝对定位,Portal实在是简易友好得多。
场景
对话框,确认提示框,悬浮窗这些组件,一般都要做一个比当前视图层层级更高的View,但是现有的方案都很难跳出父容器的约束。如何破除这个约束?
Portal提供了一种将子节点渲染到存在于父组件以外的DOM节点的优秀的方案
子节点可以被渲染到父组件之外的DOM上,这不就是解除了这个约束了吗?
Portal:传送门。steam上有一款名为 Portal2 的游戏,互相利用传送门进行位移的配合闯关游戏,传送的含义和这里颇为相似,不过这里是“传送”组件。
使用和效果
普通的 render 方法就是将组件内的元素挂载到最近的DOM节点中。而 Portal 则通过一个API,将元素挂载到任意有效的DOM节点上。
ReactDOM.createPortal( children,container,key? )
具体参数类型如下:

现在开始在代码中使用:
在
public目录下的index.html文件中,设置<div id="modal"></div>。后面 React 元素会被放到modal这个div里,这个DOM元素就是container。<div id="root"></div>
<div id="modal"></div>
对该 API 做简单封装,构建
Modal组件。因为Portal API是读children子组件的,所以Portal肯定是作为容器来用。(这里直接操作操作了
children,你也可以像Portal文档一样,在其中多加一个div层级隔离)const modalDivElement = document.getElementById("modal"); export default function Modal({ children }) {
return ReactDOM.createPortal(children, modalDivElement);
}
使用
createPortal API,Modal 组件中的子元素children会被渲染到modalDivElement中,而不管你的Modal在何处使用。使用
Modal组件。在红色背景div中嵌入了Modal,Modal中只有一个div字符串zhangsan。export default function App() {
return (
<div style={{ backgroundColor: "#a00", width: `200px`, height: `100px` }}>
<Modal>
<div>zhangsan</div>
</Modal>
</div>
);
}
现在来看看效果:

如果没有
Modal层级,zhanghsan是应该在红色区域里面的。但是为什么加了Modal就在外面了?这时检查渲染结构,秘密就在这里:
可以看到,
Modal中的元素被渲染到了id="modal"的这个div里。来回顾一下发生了什么,我们的代码组件结构是这样:<div id="root">
<div style={{ backgroundColor: "#a00", width: `200px`, height: `100px` }}>
<Modal>
<div>zhangsan</div>
</Modal>
</div>
</div>
<div id="modal"></div>
但是实际的渲染结构却是这样(观察其中
Modal子元素的变化):<div id="root">
<div style={{ backgroundColor: "#a00", width: `200px`, height: `100px` }</div>
</div>
<div id="modal">
<div>zhangsan</div>
</div>
实际渲染结构和代码结构并不一致了 ,是因为
Modal中的Portal将子元素方法放到了指定的container中。子元素被跨层级渲染,就像被传送过去一样,这便是Portal。
制作一个 Modal 弹出框组件
上面只是演示了Portal传送子组件的效果,如果要做到弹出蒙层的效果,只需要在需要传送的children子元素上添加一个div包裹并添加样式,如果弹出层样式一致,可以直接Modal组件中,当然也可以在具体的children中实现并自定义样式。
实例:

例子代码:
export default function App() {
const [isShow, setIsShow] = useState(false);
function click(e) {
console.info("e", e);
setIsShow(!isShow);
}
return (
<div
style={{
backgroundColor: "#a00",
width: `200px`,
height: `100px`,
}}
onClick={click}
>
{isShow && (
<Modal>
<span>zhangsan</span>
</Modal>
)}
</div>
);
}
Modal组件:
const modalDivElement = document.getElementById("modal");
export default function Modal({ children }) {
const modalContent = (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: `rgba(0, 0, 0, 0.5)`,
}}
>
{children}
</div>
);
return ReactDOM.createPortal(modalContent, modalDivElement);
}
如果Modal交互和内容切换较多,可以进一步封装后通过ref命令式的调用,动态传入Modal和子组件切换。
Portal 事件冒泡
Portal中的事件冒泡是遵从React结构的,并不是实际渲染的DOM元素结构,也就是说上面的root节点可以获取到modal中冒泡出来的事件。
而,上面的页面中的click事件,可以获取点击红色区域触发Modal的事件,也能获取到Modal内部的点击span的事件:

这样,Modal中的交互控制并没有脱离当前的页面或组件,和没有使用Portal时的事件表现一致。
React Portal - 弹出层的优秀解决方案的更多相关文章
- layer弹出层不居中解决方案
layer弹出层不居中解决方案 代码头中加入以下代码即可 <!doctype html>
- layer弹出层不居中解决方案,layer提示不屏幕居中解决方法,layer弹窗不居中解决方案
layer弹出层不居中解决方案,layer提示不屏幕居中解决方法,layer弹窗不居中解决方案 >>>>>>>>>>>>> ...
- layer弹出层不居中解决方案,仅显示遮罩,没有弹窗
问题:项目中layer询问层的弹窗仅显示遮罩层,并不显示弹窗…… 原因:图片太多将layer弹窗挤出屏幕下方,看不见了…… 解决方案:让layer的弹出层居中显示 一.问题描述 用layer做操作结果 ...
- layer弹出层不居中解决方案(转)
@感谢参考文章 原文内容: 一.问题描述 用layer做操作结果提示时,发现如果页面超出屏幕的高度时,弹出的提示不是屏幕居中,而是在页面高度的中间,如果一个页面的高度比较大,就看不到提示了. 还有一种 ...
- react学习之弹出层
react的弹出层不同于以往的DOM编程,我们知道,在DOM中,弹出层事件绑定在对应的节点上即可,但是在react中,往往只能实现父子之间的传递控制,显然,弹出层的层级不符合此关系. 在这里我们需要使 ...
- layer 弹出层 不居中
layer弹出层不居中解决方案 代码头中加入以下代码即可 <!doctype html>
- React native 的弹出层(输入)效果
/*弹出层测试*/ import React,{Component} from 'react'; import { StyleSheet, View, Image, Text, TouchableOp ...
- 利用React/anu编写一个弹出层
本文将一步步介绍如何使用React或anu创建 一个弹出层. React时代,代码都是要经过编译的,我们很多时间都耗在babel与webpack上.因此本文也介绍如何玩webpack与babel. 我 ...
- react 点击空白处隐藏弹出层
点击空白处隐藏弹出层的原理是:在 document 上绑定事件来隐藏弹出层,这样点击任何元素的时候都会冒泡到 document 上,都会执行隐藏弹出层的功能.然后我们在不需要隐藏弹出层的元素上阻止冒泡 ...
随机推荐
- Vue技术点整理-指令
我们通常给一个元素添加 v-if / v-show 来做权限管理,但如果判断条件繁琐且多个地方需要判断,这种方式的代码不仅不优雅而且冗余. 针对这种情况,我们可以通过全局自定义指令来处理:我们先在新建 ...
- FridaHook框架学习(2)
FridaHook框架学习(2) 前言 学习过程参考https://bbs.pediy.com/thread-227233.htm. 逆向分析 安装并运行例子程序,可以看到这个例子是一个验证注册码的程 ...
- 自己动手实现java断点/单步调试(一)
又是好长时间没有写博客了,今天我们就来谈一下java程序的断点调试.写这篇主题的主要原因是身边的公司或者个人都执着于做apaas平台,简单来说apaas平台就是一个零代码或者低代码的配置平台,通过配置 ...
- ScalikeJDBC,操作mysql数据,API
ScalikeJDBC,操作mysql数据,API 一.构建maven项目,添加pom.xml依赖 二.resource文件下创建application.conf文件,并配置以下内容 三.操作mysq ...
- 调用个别f5 负载端口为80的vs时,返回值为空的问题
现状: vs负载端口为80并添加XFF,pool包含2个member,member的monitor端口为80&9000. 故障现象: 应用同事描述说再完全复制了一个member并添加到pool ...
- 为何 JVM TLAB 在线程退还给堆的时候需要填充 dummy object
TLAB 全网最硬核的解析,请参考:全网最硬核 JVM TLAB 分析 TLAB 在何时退还给堆? 有两种情况: 当前 TLAB 不足分配,并且剩余空间小于当前线程最大浪费空间限制时. 发生 GC 时 ...
- FunnyXEN
For any positive integer n, we define function F(n) and XEN(n).For a collection S(n)={1,2,...,2n}, w ...
- CF-1451 E Bitwise Queries 异或 交互题
E - Bitwise Queries 传送门 题意 有一组序列,长度为 \(n(4\le n \le 2^{16})\),且 \(n\) 为 2 的整数次幂,序列中数值范围为 [0,n-1], 每次 ...
- Codeforces Round #622 (Div. 2) C2. Skyscrapers (hard version)(单调栈,递推)
Codeforces Round #622 (Div. 2) C2. Skyscrapers (hard version) 题意: 你是一名建筑工程师,现给出 n 幢建筑的预计建设高度,你想建成峰状, ...
- P4074 [WC2013]糖果公园 树上莫队带修改
题目链接 Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园游玩. 糖果公园的结构十分奇特,它由 nn 个游览点构 ...