ReactDOM.render和ReactDOM.createPortal的区别

背景

最近在封装提醒组件时,因为提醒组件元素是动态创建的,需要添加在当前组件的外部(不在root中),因为这种提醒的组件是全局使用的,和业务关系不大,并且是根据需要动态使用;
发现有用 ReactDOM.createPortal,而有的使用 ReactDOM.render,于是在网上找这俩的区别,也没很好的资料。于是详细的看了官网,并写了一些 demo 来测试和验证自己的理解。

定义

render()

1
ReactDOM.render(element, container[, callback])

在提供的 container 里渲染一个 React 元素,并返回对该组件的引用(或者针对无状态组件返回 null)。

如果 React 元素之前已经在 container 里渲染过,这将会对其执行更新操作,并仅会在必要时改变 DOM 以映射最新的 React 元素。

如果提供了可选的回调函数,该回调将在组件被渲染或更新之后被执行。

createPortal()

1
ReactDOM.createPortal(child, container)

创建 portal。Portal 将提供一种将子节点渲染到 DOM 节点中的方式,该节点存在于 DOM 组件的层次结构之外
Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。

疑问

render()目前没有什么疑问,主要是 createPortal 的描述有些奇怪。
1.“该节点存在于 DOM 组件的层次结构之外”是什么意思? 2.难道使用 render 不能把子节点渲染到父组件以外的 DOM 节点吗? 3.为什么说 portal 的这个方案“优秀”? 4.两者区别是什么?

这四个问题你都知道答案,就没必要往下看了。

解答

1.“该节点存在于 DOM 组件的层次结构之外”是什么意思?

这个很好理解,通常我们是把组件都渲染在页面上一个idrootdiv容器中。这里所说的“之外”的意思,就是这个组件不渲染在root中,在一个新的容器中,也可以直接放到body最后。

2.难道使用 render 不能把子节点渲染到父组件以外的 DOM 节点吗?

!!!之前一直以为 render 只能使用一次,或者不能在额外的节点重新进行渲染,结果都不是。

3.为什么说 portal 的这个方案“优秀”? 两者的区别是什么?

既然 render 也能在新的容器中渲染组件,为什么还要再新增一个 createPortal 方法呢,意义在哪?

意义在捕获事件,事件冒泡就不详细解释了。在使用 createPortal 渲染时,比如组件 A,虽然它渲染在一个新的元素中,但调用 A 的组件 B 是可以捕获 A 中的事件。如果使用 render 就无法捕获。

比如 B 的根元素上有一个 click 事件,如果使用 render 渲染 A,点击 A 的区域,B 的 click 事件是不会触发的;但如果使用 createPortal,则会触发。

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import React from "react";
import ReactDOM from "react-dom";

let root;
class TestPortal extends React.PureComponent {
constructor(props) {
super(props);
root = document.getElementById("root1");
}
componentDidMount() {}

render() {
// createPortal换成render测试
return ReactDOM.createPortal(this.props.children, root);
}
}

export default TestPortal;

// 在APP.js中调用
<div
onClick={() => {
alert("触发了");
}}
>
hello
<TestPortal>
<h1>一个新的元素</h1>
</TestPortal>;
</div>;

这就是 createPortal 的奇特之处,虽然和父组件不在一个 dom 分支上,事件却会别捕获。

什么时候使用createPortal

一个 portal 的典型用例是当父组件有 overflow: hidden 或 z-index 样式时,但你需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框;

文章作者: wenmu
文章链接: http://blog.wangpengpeng.site/2020/05/20/ReactDOM-render%E5%92%8CReactDOM-createPortal%E7%9A%84%E5%8C%BA%E5%88%AB/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 温木的博客
微信打赏