这个答案中有一个模态https://stackoverflow.com/a/26789089/883571它通过将其附加到<body>
. 但是,我发现它与 React 提供的转换插件不兼容。
如何创建一个过渡(在进入和离开期间)?
这个答案中有一个模态https://stackoverflow.com/a/26789089/883571它通过将其附加到<body>
. 但是,我发现它与 React 提供的转换插件不兼容。
如何创建一个过渡(在进入和离开期间)?
在 react conf 2015 上,Ryan Florence演示了使用门户。以下是创建简单Portal
组件的方法...
var Portal = React.createClass({
render: () => null,
portalElement: null,
componentDidMount() {
var p = this.props.portalId && document.getElementById(this.props.portalId);
if (!p) {
var p = document.createElement('div');
p.id = this.props.portalId;
document.body.appendChild(p);
}
this.portalElement = p;
this.componentDidUpdate();
},
componentWillUnmount() {
document.body.removeChild(this.portalElement);
},
componentDidUpdate() {
React.render(<div {...this.props}>{this.props.children}</div>, this.portalElement);
}
});
然后你通常可以在 React 中做的所有事情你都可以在门户内做......
<Portal className="DialogGroup">
<ReactCSSTransitionGroup transitionName="Dialog-anim">
{ activeDialog === 1 &&
<div key="0" className="Dialog">
This is an animated dialog
</div> }
</ReactCSSTransitionGroup>
</Portal>
你也可以看看 Ryan 的react-modal,虽然我还没有真正使用过它,所以我不知道它在动画中的效果如何。
我编写了应该对您有所帮助的modulereact-portal。
用法:
import { Portal } from 'react-portal';
<Portal>
This text is portaled at the end of document.body!
</Portal>
<Portal node={document && document.getElementById('san-francisco')}>
This text is portaled into San Francisco!
</Portal>
这是本文中描述的方法的 ES6 版本:
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
export default class BodyEnd extends React.PureComponent {
static propTypes = {
children: PropTypes.node,
};
componentDidMount() {
this._popup = document.createElement('div');
document.body.appendChild(this._popup);
this._render();
}
componentDidUpdate() {
this._render();
}
componentWillUnmount() {
ReactDOM.unmountComponentAtNode(this._popup);
document.body.removeChild(this._popup);
}
_render() {
ReactDOM.render(this.props.children, this._popup);
}
render() {
return null;
}
}
只需用它包装任何你想放在 DOM 末尾的元素:
<BodyEnd><Tooltip pos={{x,y}}>{content}</Tooltip></BodyEnd>
这是 React 16 的更新版本:
import React from 'react';
import ReactDOM from 'react-dom';
export default class BodyEnd extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement('div');
this.el.style.display = 'contents';
// The <div> is a necessary container for our
// content, but it should not affect our layout.
// Only works in some browsers, but generally
// doesn't matter since this is at
// the end anyway. Feel free to delete this line.
}
componentDidMount() {
document.body.appendChild(this.el);
}
componentWillUnmount() {
document.body.removeChild(this.el);
}
render() {
return ReactDOM.createPortal(
this.props.children,
this.el,
);
}
}
正如其他答案所述,这可以使用门户来完成。从v16.0
门户开始包含在 React 中。
<body>
<div id="root"></div>
<div id="portal"></div>
</body>
通常,当您从组件的 render 方法返回一个元素时,它会作为最近父节点的子节点挂载到 DOM 中,但是通过门户,您可以将子节点插入到 DOM 中的不同位置。
const PortalComponent = ({ children, onClose }) => {
return createPortal(
<div className="modal" style={modalStyle} onClick={onClose}>
{children}
</div>,
// get outer DOM element
document.getElementById("portal")
);
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
modalOpen: false
};
}
render() {
return (
<div style={styles}>
<Hello name="CodeSandbox" />
<h2>Start editing to see some magic happen {"\u2728"}</h2>
<button onClick={() => this.setState({ modalOpen: true })}>
Open modal
</button>
{this.state.modalOpen && (
<PortalComponent onClose={() => this.setState({ modalOpen: false })}>
<h1>This is modal content</h1>
</PortalComponent>
)}
</div>
);
}
}
render(<App />, document.getElementById("root"));
在此处检查工作示例。
这里的基本问题是,在 React 中,您只允许将组件挂载到其父级,这并不总是所需的行为。但是如何解决这个问题呢?
我已经制定了解决方案,旨在解决此问题。更详细的问题定义、源代码和示例可以在这里找到:https : //github.com/fckt/react-layer-stack#rationale
基本原理
react
/react-dom
带有 2 个基本假设/想法:
- 每个 UI 都是自然分层的。这就是为什么我们有
components
相互包裹的想法react-dom
默认情况下(物理上)将子组件挂载到其父 DOM 节点问题是有时第二个属性不是您想要的。有时你想将你的组件挂载到不同的物理 DOM 节点,同时保持父子节点之间的逻辑连接。
规范示例是类似工具提示的组件:在开发过程的某个时刻,您可能会发现您需要为您添加一些描述
UI element
:它将在固定层中呈现并且应该知道其坐标(即UI element
坐标或鼠标坐标)和同时它需要信息是否需要立即显示,它的内容和来自父组件的一些上下文。此示例显示有时逻辑层次结构与物理 DOM 层次结构不匹配。
看看https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example看看具体的例子,它是你的问题的答案:
import { Layer, LayerContext } from 'react-layer-stack'
// ... for each `object` in array of `objects`
const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
return (
<Cell {...props}>
// the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
<Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
hideMe, // alias for `hide(modalId)`
index } // useful to know to set zIndex, for example
, e) => // access to the arguments (click event data in this example)
<Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
<ConfirmationDialog
title={ 'Delete' }
message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
confirmButton={ <Button type="primary">DELETE</Button> }
onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
close={ hideMe } />
</Modal> }
</Layer>
// this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
<LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
<div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
<Icon type="trash" />
</div> }
</LayerContext>
</Cell>)
// ...