我正在使用 React 16 并需要门户网站,但找不到有关此功能的任何文档。有谁知道如何使用这个吗?
https://github.com/facebook/react/pull/10675
感谢您的任何建议。
我正在使用 React 16 并需要门户网站,但找不到有关此功能的任何文档。有谁知道如何使用这个吗?
https://github.com/facebook/react/pull/10675
感谢您的任何建议。
React v16 几个小时前刚刚发布(耶!!),它正式支持Portal
.
什么是门户?它已经存在多久了?
Portals 提供了一种一流的方式来将子组件渲染到存在于父组件的 DOM 层次结构之外的 DOM 节点中。
Portal
在 React 社区中并不是新概念。许多库都支持这种功能。例如react-portal和react-gateway。
渲染任何react应用程序时会发生什么?
通常,在渲染任何 React 应用程序时,都会使用单个 DOM 元素来渲染整个 React 树。
class HelloReact extends React.Component {
render() {
return (
<h1>Hello React</h1>
);
}
}
ReactDOM.render(<HelloReact />, document.getElementById('root'));
如您所见,我们正在将我们的 react 组件渲染为具有 id 的 DOM 元素root
。
什么是 Portal,为什么需要它?为什么会在那里?
Portals 是一种在父组件的主 DOM 层次结构之外呈现 React 子组件的方法,而不会丢失React上下文。我强调它是因为非常流行的库,比如react-router,redux大量使用 react 上下文。所以使用时的上下文可用性Portal
非常有帮助。
根据react文档,
门户的典型用例是父组件具有溢出:隐藏或 z-index 样式,但您需要子组件在视觉上“突破”其容器。例如,对话框、悬停卡和工具提示。
因此,使用门户网站,您可以在需要时将并行react树渲染到另一个 DOM 节点上。即使它在不同的 DOM 节点中呈现,父组件也可以捕获未捕获的事件。请参阅文档本身中提供的此代码笔。
下面的例子应该给你更多的想法:
// index.html
<html>
<body>
<div id="root"></div>
<div id="another-root"></div>
</body>
</html>
// index.jsx
const mainContainer = document.getElementById('root');
const portalContainer = document.getElementById('another-root');
class HelloFromPortal extends React.Component {
render() {
return (
<h1>I am rendered through a Portal.</h1>
);
}
}
class HelloReact extends React.Component {
render() {
return (
<div>
<h1>Hello World</h1>
{ ReactDOM.createPortal(<HelloFromPortal />, portalContainer) }
</div>
);
}
}
ReactDOM.render(<HelloReact />, mainContainer);
https://codesandbox.io/s/62rvxkonnw
您可以使用 devtools 检查元素并查看它<h1>I am rendered through a Portal.</h1>
在#another-root
标签内部呈现,而<h1>Hello World</h1>
在#root
标签内部呈现。
希望这可以帮助 :)
更新:回答@PhillipMunin 的评论。
ReactDOM.render
和 之间有什么区别ReactDOM.createPortal
?
即使通过门户呈现的组件在其他地方呈现(在当前容器根之外),它仍然作为同一父组件的子组件存在。(谁调用了ReactDOM.createPortal
)因此子级上的任何事件都会传播到父级。(Ofc,如果您手动停止事件的传播,这将不起作用。)
在通过门户呈现的组件内部可以访问相同的上下文。但不是在我们ReactDOM.render
直接做的情况下。
我创建了另一个演示来说明我的观点。https://codesandbox.io/s/42x771ykwx
我认为我们可以通过在“根”元素之外创建一个外部 dom 节点来实现相同的功能
node = document.createElement('div')
document.body.appendChild(node)
ReactDOM.render(<Modal {...props} />,node)
假设我们正在创建一个模态视图,我们可以为它创建一个包装器
import React, { useEffect } from 'react'
import ReactDOM from 'react-dom'
import Modal from './Modal'
let node = null
const ModalWrapper = (props) =>{
useEffect(()=>{
node && ReactDOM.render(<Modal {...props} />,node)
})
useEffect(()=>{
node = document.createElement('div')
document.body.appendChild(node)
ReactDOM.render(<Modal {...props} />,node)
},[])
return(
<script />
)
}
对于任何想知道的人,这是我如何在 Typescript 中实现我自己的自定义 Portal 组件
import React from 'react'
import ReactDOM from 'react-dom'
const Portal: React.FC<PortalProps> = (props) => {
return ReactDOM.createPortal(props.children, props.target)
}
export interface PortalProps {
target: Element;
children: React.ReactNode;
}
export default Portal
门户是通过调用组件createPortal
方法内部的render
方法创建的。
render() {
return (
<div>
{ReactDOM.createPortal(this.renderPortal(), this.props.portalEl)}
</div>
)
}
renderPortal
应该返回要在门户内部呈现的内容,而portalEl
是将接收内容的外部 DOM 元素。
最近有人发推说,可以在React 测试中找到有关门户的信息。