Update:React 16.0至ReactDOM.createPortal
link
Update:React的下一个版本(光纤:可能是16或17)将包括一种创建入口的方法:ReactDOM.unstable_createPortal()
link
Use portals
丹·阿布拉莫夫的回答第一部分很好,但涉及很多样板.正如他所说,你也可以使用门户网站.我会详细介绍一下这个 idea .
门户的优势在于弹出窗口和按钮与react 树保持非常接近,使用props 进行非常简单的父/子通信:您可以轻松地处理与门户的异步操作,或者让父门户自定义门户.
什么是门户?
门户允许您在document.body
内直接呈现深度嵌套在您的react 树中的元素.
例如,可以将以下react 树渲染到实体中:
<div className="layout">
<div className="outside-portal">
<Portal>
<div className="inside-portal">
PortalContent
</div>
</Portal>
</div>
</div>
你得到的结果是:
<body>
<div class="layout">
<div class="outside-portal">
</div>
</div>
<div class="inside-portal">
PortalContent
</div>
</body>
inside-portal
node 已在<body>
内部转换,而不是其正常的深嵌套位置.
When to use a portal
门户对于显示应该位于现有Reaction组件之上的元素特别有用:弹出窗口、下拉菜单、建议、热点
Why use a portal
No z-index problems anymore:门户允许渲染到<body>
.如果你想显示一个弹出窗口或下拉列表,如果你不想对抗z索引问题,这是一个非常好的主意.门户元素以装入顺序添加到dodocument.body
,这意味着除非您使用z-index
,否则默认行为将是以装入顺序将门户堆叠在彼此之上.实际上,这意味着您可以从另一个弹出窗口中安全地打开一个弹出窗口,并确保第二个弹出窗口将显示在第一个弹出窗口的顶部,而无需考虑z-index
.
In practice
Most simple: use local React state:如果您认为,对于一个简单的删除确认弹出窗口,使用Redux样板文件是不值得的,那么您可以使用一个门户,它大大简化了您的代码.对于这样的用例,交互是非常局部的,实际上是一个相当详细的实现细节,您真的关心热重新加载、时间旅行、操作日志(log)以及Redux带给您的所有好处吗?就我个人而言,在这种情况下,我不会使用本地州.代码变得非常简单:
class DeleteButton extends React.Component {
static propTypes = {
onDelete: PropTypes.func.isRequired,
};
state = { confirmationPopup: false };
open = () => {
this.setState({ confirmationPopup: true });
};
close = () => {
this.setState({ confirmationPopup: false });
};
render() {
return (
<div className="delete-button">
<div onClick={() => this.open()}>Delete</div>
{this.state.confirmationPopup && (
<Portal>
<DeleteConfirmationPopup
onCancel={() => this.close()}
onConfirm={() => {
this.close();
this.props.onDelete();
}}
/>
</Portal>
)}
</div>
);
}
}
Simple: you can still use Redux state:如果你真的想,你仍然可以用connect
来 Select 是否显示DeleteConfirmationPopup
.由于门户仍然深入嵌套在React树中,因此定制此门户的行为非常简单,因为您的父级可以向门户传递props .如果不使用门户,出于z-index
个原因,通常需要在React树的顶部呈现弹出窗口,并且通常需要考虑"如何自定义根据用例生成的通用DeleteConfirmationPopup".通常,你会发现这个问题的解决方案相当粗糙,比如调度一个包含嵌套的确认/取消操作、翻译包密钥,或者更糟的是,一个渲染函数(或者其他不可分解的东西).你们不必对门户网站这么做,只需传递常规props 即可,因为DeleteConfirmationPopup
只是DeleteButton
岁的子元素
结论
门户对于简化代码非常有用.我不能再没有他们了.
请注意,门户实现还可以帮助您实现其他有用的功能,如:
- 可达性
- 使用快捷方式关闭门户
- 处理外部点击(是否关闭门户)
- 处理链接点击(是否关闭门户)
- 门户树中提供的React上下文
react-portal或react-modal适用于弹出窗口、模态和覆盖,它们应该是全屏的,通常居中于屏幕中央.
react-tether对于大多数React开发者来说是未知的,但它是你能找到的最有用的工具之一.Tether允许您创建入口,但会相对于给定目标自动定位入口.这是完美的工具提示,下拉列表,热点,帮助框...如果你曾经遇到过位置absolute
/relative
和z-index
的任何问题,或者你的下拉列表超出了你的视口,Tether将为你解决所有这些问题.
例如,您可以轻松实现入职热点,单击后可扩展为工具提示:
个
这里是真正的生产代码.不能再简单了:)
<MenuHotspots.contacts>
<ContactButton/>
</MenuHotspots.contacts>
Edit:刚刚发现react-gateway个允许将门户渲染到您 Select 的 node (不一定是主体)
Edit:似乎react-popper是一个不错的 Select ,以react 系绳.PopperJS是一个库,它只计算元素的适当位置,而不直接接触DOM,让用户 Select 何时何地放置DOM node ,而Tether直接附加到主体.
Edit:还有react-slot-fill个,这很有趣,可以通过允许将元素呈现到保留的元素槽中来帮助解决类似的问题,该保留的元素槽可以放在树中的任何位置