React-dom中的Portals😊👉

Portals

Portals 被翻译成传送门,它要做的事情实际上也确实和传送门很相似,根据官方的解释:

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。

其语法为:

js 复制代码
ReactDOM.createPortal(child, container)

第一个参数(child )是任何可渲染的 React 子元素,第二个参数(container )是一个 DOM 元素。

学习一个知识我们仍然是应该从以下 2 个点着手:

  • 何时用?
  • 如何用?

什么场景下需要使用 Portals

首先我们来看一个场景,如下:

js 复制代码
// App.jsx
import { useState } from "react";
import Modal from "./components/Modal"
function App() {

  const [isShow, setIsShow] = useState(false);
  return (
    <div>
      <h1>App组件</h1>
      <button onClick={()=>setIsShow(!isShow)}>显示/隐藏</button>
      {isShow ? <Modal /> : null}
    </div>
  );
}

export default App;
js 复制代码
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"
        }}>模态框</div>
    )
}

export default Modal;

在上面的示例中,Modal 是一个模态框,在 App 根组件中能够控制该模态框组件是否显示。

上面的示例,功能倒是没有什么问题,但是从最终渲染出来的 html 结构上来讲,将整个模态框都放在 root 这个 div 中不是那么合适,我们生成的 html 结构上,这个模态框能够渲染到 modal 那个 div 里面。

并且一旦父组件上面设置了额外的样式,都会影响这个子组件的渲染,例如:

js 复制代码
<div style={{
  position: "relative"
}}>
  <h1>App组件</h1>
  <button onClick={() => setIsShow(!isShow)}>显示/隐藏</button>
  {isShow ? <Modal /> : null}
</div>

我们在 App 组件中添加一条相对定位的样式,此时我们就会发现由于 Modal 是放在整个 root 元素里面的,模态框的位置就会收到影响。

因此,在这种时候,我们就可以使用 Portals 来解决这个问题。

如何使用 Portals

Portals 的使用方式也非常简单,只需要使用 createPortal 方法来指定渲染到哪个元素中即可。需要注意的是这是和 React 渲染相关的,所以 createPortal 方法来自于 react-dom 这个库。

js 复制代码
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"
    }}>模态框</div>),document.getElementById("modal"))
}

export default Modal;

在上面的代码中,我们将要渲染的视图作为 createPortal 方法的第一个参数,而第二个参数用于指定要渲染到哪个 DOM 元素中。

可以看到,这一次模态框就被渲染到了 id 为 modal 的 div 中。并且在 root 中所设置的样式都不会影响到模态框的显示。

其实根据官方的介绍,Portals 的典型用例是当父组件有 overflow: hidden 或 z-index 样式时,但你需要子组件能够在视觉上"跳出"其容器。例如,对话框、悬浮卡以及提示框。

通过 Portal 进行事件冒泡

最后需要注意一下的就是使用 Portal 所渲染的元素在触发事件时的冒泡问题。

以上面的例子为例,看上去模态框已经渲染到了 modal 这个元素里面,但是在 React 中事件冒泡是按照组件结构来进行冒泡的,我们可以看到即使模态框已经渲染到了 modal 里面,但是在组件树中模态框组件仍然是在根组件中。

我们也可以书写一个例子来验证一下,例如我们为 App 根组件绑定一个点击事件,如下:

js 复制代码
import { useState } from "react";
import Modal from "./components/Modal"
function App() {

  const [isShow, setIsShow] = useState(false);
  return (
    <div style={{
      position: "relative"
    }} onClick={()=>console.log("App 组件被点击了")}>
      <h1>App组件</h1>
      <button onClick={() => setIsShow(!isShow)}>显示/隐藏</button>
      {isShow ? <Modal /> : null}
    </div>
  );
}

export default App;

之后我们点击模态框,会发现仍然是能够触发 App 根组件的点击事件。

正如官方文档所说:

尽管 portal 可以被放置在 DOM 树中的任何地方,但在任何其他方面,其行为和普通的 React 子节点行为一致。由于 portal 仍存在于 React 树, 且与 DOM 树中的位置无关,那么无论其子节点是否是 portal ,像 context 这样的功能特性都是不变的。

这包含事件冒泡。一个从 portal 内部触发的事件会一直冒泡至包含 React 树的祖先,即便这些元素并不是 DOM 树中的祖先。
最后说一句如果要封装Modal组件的话,可以使用HTML原生的<dialog>元素,样式也可以自己定制😍😍

link :developer.mozilla.org/en-US/docs/...

相关推荐
IT_陈寒2 小时前
Java中equals方法比了个寂寞?原来这才是正确的重写姿势
前端·人工智能·后端
默_笙2 小时前
🚓 分诊台与拆题术:让 RAG 学会判断和规划
前端·javascript
CopyCode2 小时前
用 AI 迁项目有多爽?我把 Webpack 迁 Vite 的全过程记下来了
前端·架构
去伪存真2 小时前
Electron 自动化发布指南:GitHub Actions 跨平台打包全纪录
前端·electron
计算机魔术师2 小时前
OpenAI智能体失控闯进美国政府网站,53张用户图片外泄背后
前端
颜进强2 小时前
14 · NestJS ExecutionContext 执行上下文:守卫、拦截器、过滤器拿到的"同一个 context",为什么能力不一样?
前端·后端·ai编程
程序员Flycan2 小时前
🚀 跨域终结者:前端代理服务器(Proxy)原理解析与配置总结
前端
怕浪猫2 小时前
顶级模型一句话,AI 写出了能玩的 QQ飞车
前端·面试·github
huakoh2 小时前
MCP 报错分不清?先看响应里是 result 还是 error
前端
呃呃呃呃ex2 小时前
10. 现代前端工程化:ES6 React 项目实战与原理解析
前端·javascript