react传送门createPortal

3,205 阅读3分钟

最近学习的时候,看到一个比较有意思的api,一开始看比较懵逼,后来看了程摩Morgan大佬的文章(文章在这里),感觉豁然开朗,故记录一下。

  • 什么是传送门?

传送门的意思就是,组件从一个门进去,然后从另一个门出去。举个栗子,假如在我们生活的空间里有一个传送门,然后再天堂也有一个传送门,当我们从我们这边的传送门进去,我们就可以到天堂了。

  • 为什么要传送门?

这个时候可能有人就会说了:“哎呀,你说的我都懂,你就直接告诉我它有啥用吧!" 不知道大家在开发的过程中有没有需要开发对话框的场景,大家都知道,对于用户的感觉来说,对话框应该是一个独立的组件,同时应该脱离文档流居中,并且只有在用户做对用的操作的时候才显示出来。假如我们有很多个页面,然后每个页面都有对话框,这时候大家肯定会想把对话框单独抽出来做成一个组件,但是既然需要每个页面都要用到,所以这个组件应该独立性很高,但是当我们把它放到具体的页面的时候,它可能是当前这个页面的子组件,然后当你对他定位的时候,可能会发现对话框不听话了,这是因为绝对定位是按照父组件的第一个非static进行定位的,你没办法保证它的左右父组件都是static。当然你也可以像上面文章中提到的用其它的方法解决这一问题,但是会比较复杂,我这边就不赘述了,感兴趣可以看看大佬的文章。

  • 如何使用?

我这边直接在root下面手动添加了一个节点,后面我们将会把对话框挂到这个节点上。

![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/87cf3ec96bc5436eb63f4dcbf84a8b68~tplv-k3u1fbpfcp-zoom-1.image)

下面是App.js的代码

import React, { useState } from "react";
import "./styles.css";
import Button from "./father";
export default function App() {
  const [showDialog, setShowDialog] = useState(false);
  function changeShowDialog() {
    setShowDialog(!showDialog);
  }
  return (
    <div className="App">
      <h1>This is App</h1> 
      <Button showDialog={showDialog} changeShowDialog={changeShowDialog} />
    </div>
  );}

father.ja代码

import React from "react";
import Dialog from "./portal";
function Button(props) {
  const { showDialog, changeShowDialog } = props;
  return (
    <div className="father" onClick={changeShowDialog}> //用来接收子组件的冒泡
      <button onClick={changeShowDialog}>open</button>
      <div onClick={() => console.log("传送出来了")}> //用来接收子组件的冒泡
        {showDialog ? <Dialog /> : null}
      </div>
    </div>  );
}
export default Button;

protal.js代码

import { createPortal } from "react-dom";
import React from "react";
function Dailog() {
  return createPortal(
    <div className="dialog"> // 对话框
      <h1>我是弹窗</h1>
      <button>关闭</button>
    </div>,
    document.getElementById("dialog") // 将挂载的root同级节点
  );
}
export default Dailog;

styles.css

.App {    
  font-family: sans-serif;    
  text-align: center;    
}    
.father {    
  width: 500px;    
  height: 300px;    
  background-color: red;    
  position: relative;    
}    
.dialog {    
  width: 200px;    
  height: 100px;    
  background-color: aqua;    
  position: absolute;    
  top: 50%;    
  left: 50%;    
  transform: translate3d(-50%, -50%, 0);    
}    

演示效果

对话框的效果大概就这样了,因为它挂载到了界面的另一个地方,所以它的定位不会受到root节点下定位的影响,同时值得一提的是,它不仅可以将一个组件传送到另一个组件,这个组件被传送的组件的事件却还可以冒泡回去它得父节点,就相当于我穿越到了天上,但是我在天上拉的屎还可以通过传送门传回人间,很神奇。

怎么样,是不是很神奇有用的的一个api,如果觉得我这边说的不是很清楚,那你可以去看看大佬的文章,如果觉得我有说的不对的地方希望可以不吝赐教。