来自react portal的文档:
门户网站的典型用例是父组件出现溢出:隐藏或z-index样式,但是您需要子组件在视觉上“突破”其容器。例如,对话框,悬浮卡和工具提示。
建议的解决方案是使用:
// this line is rendered in `Portal1` component,
// which is rendered in `Parent1` component.
ReactDOM.createPortal(Child1, Container1)
我不知道它能解决什么。为什么让Child1
的{{1}}代替Container1
成为孩子?
我的问题可能不清楚,如果不是->此解决方案与其他用于创建“对话框,悬浮卡和工具提示”的解决方案有何不同?
答案 0 :(得分:3)
在React V15中,我们只能将子级dom添加到父级dom中,这意味着,如果要具有一个元素,则必须创建一个新的div。像这样:
<div>
{this.props.children}
</div>
在React v16中,不需要创建新的div。我们可以使用门户网站将children元素添加到dom树中的任何dom中。
ReactDOM.createPortal(
this.props.children,
domNode
);
如果父级组件有溢出:隐藏或z-index样式,而子级元素类型是对话框,悬浮卡,工具提示等,则这些元素应位于父级元素的上层,表示中断。它们可能被父组件遮住了。
因此createPortal提供了一个更好的选择。它可以加载到父组件的上部组件上。将元素安装到另一个dom后,将不再受到保护。
即使该组件已安装在另一个组件上,事件也可能上升到父组件。
答案 1 :(得分:2)
初始化React应用程序时,ReactDOM
会问一个DOM
容器,您所有的React组件都将在此DOM
下呈现。这使得React可以处理所有渲染过程。
但是有时您需要控制一些React组件以在不同的DOM
元素下呈现并与您现有的React应用程序进行交互。在这种情况下,React Portal十分方便。
当React在底层创建虚拟元素时,您不能将它们转换为DOM
元素并将其直接插入DOM
中。 React Portals允许您传递React Elements并为其指定容器DOM。
以下是演示示例
您拥有Modal
组件,该组件将div
元素呈现在中心。
function Modal() {
return (
<div style={{ position: 'absolute', left: '50%'}}>
Message
</div>
);
}
一个将您的Modal
组件放入div
内,该组件具有相对位置。
<div style={{ position: 'relative', left: 100 }}>
<Modal />
</div>
问题在于渲染Modal
组件时,其位置相对于父级div
的位置,但是您需要在窗口中心显示它。
为了解决此问题,您可以直接将Modal
组件附加到body
元素中。使用门户网站可以轻松完成此操作。
这是门户网站的解决方案。
function ModalRenderer() {
return (
React.createPortal(
<Modal />,
document.body
)
);
}
并在应用程序内的任何地方使用ModalRenderer
组件。
<div style={{ position: 'relative', left: 100 }}>
<ModalRenderer />
</div>
这样,您的ModalRenderer
决定了Modal
的容器元素,该元素与您的应用程序树无关。
答案 2 :(得分:1)
一个很好的案例是分离CSS问题。
这里是一个例子:
HTML
A
CSS
<div id="app-root"></div>
<div id="modal-root"></div>
通天塔
.app {
position: fixed;
height: 100%;
width: 100%;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.modal {
background-color: rgba(0,0,0,0.5);
}
观察:
1.正文的位置为const appRoot = document.getElementById('app-root')
const modalRoot = document.getElementById('modal-root')
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
showModal: false
}
this.handleShow = this.handleShow.bind(this);
this.handleHide = this.handleHide.bind(this);
}
handleShow() {
this.setState({
showModal: true
})
}
handleHide() {
this.setState({
showModal: false
})
}
render() {
const modal = this.state.showModal ? (
<Modal>
<div className="modal">I am no longer centered!</div>
</Modal>
) : null
return (
<div className='app'>
Basic
<button onClick={this.handleShow}>
Show Modal
</button>
{modal}
</div>
)
}
}
class Modal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement('div');
}
componentDidMount(){
modalRoot.appendChild(this.el)
}
componentWillUnmount() {
modalRoot.removeChild(this.el)
}
render() {
return ReactDOM.createPortal(
this.props.children,
this.el
)
}
}
ReactDOM.render(<App />, appRoot)
,居中
2.当我们点击按钮时,文本弹出。请注意,文本不再居中!
3.如果我们已经将它用于类似这样的事情:
fixed
注意文本居中。模态的工作方式是转到 <div className='app'>
Basic
<button onClick={this.handleShow}>
Show Modal
</button>
<div className="modal">I am centered ;(</div>
</div>
并将该DOM元素附加到那里。您可以在modal-root
下拥有自己的CSS,并与父组件(modal-root
)分开。
您现在不再有义务将“孩子”组件附加到父组件下。在这种情况下,您(app-root
)会将其附加到同级组件({{1} }。您可以将其完全附加到app-root
或任何您想要的元素上。像其他用户提到的那样,另一个特权是,事件冒泡的发生就像该子组件是他们自己的子组件一样。
#app-root中的Parent组件将能够捕获来自同级节点#modal-root的未捕获的冒泡事件。 Source