考虑一个组件,它需要在自己的DOM树中管理不是子组件的子组件,但必须添加到顶级文档中。
一个典型的例子是自动完成字段,需要在输入字段下方的浮动菜单中显示自动完成匹配。浮动菜单必须作为文档正文元素的子元素添加,以逃避树中任何阻止它显示的“溢出:隐藏”的隐藏。浮动菜单在不再使用后需要删除。
在这种情况下,逻辑方法似乎是将组件安装在任意div中,然后在不再需要时卸载它。但是,当事件用于触发此类卸载时,这会引入一个有趣的状态流问题。
以下是我当前代码的摘录,用于说明问题:
componentDidUpdate: function(prevProps, prevState) {
if (prevState.matches !== this.state.matches) {
if (this._floater) {
this._floater.remove();
this._floater = null;
}
if (this.state.matches.length > 0) {
this._floater = Floater.create(
<Floater
parentElement={this.getDOMNode()}
open={true}>
<SelectableList
items={this.state.matches}
limit={10}
onSelectionChange={this.handleSelectionChange}/>
</Floater>
);
}
}
},
handleSelectionChange: function(items) {
this.setState({matches: [], selectedItem: items[0]});
},
这里,Floater
是一个可以包含任何其他组件的通用组件;它将自己定位为绝对,定位自己等等。 Floater.create()
是一种方便的方法,用于创建浮动组件并将其插入文档中。
Floater.remove()
目前看起来像这样:
remove: function() {
var self = this;
if (this.isMounted()) {
window.setTimeout(function() {
React.unmountComponentAtNode(self.getDOMNode().parentNode);
}, 10);
}
},
它使用超时的原因是允许父组件在状态更新后能够远程访问它。选择SelectableList
中的内容会在父级中触发handleSelectionChange
,这会调用remove()
在组件仍在使用时卸载该组件。这很难看,虽然它确实有效。
是否有更好,更惯用的方式?
答案 0 :(得分:0)
只是给这个问题的访问者提个醒。
从 React v16 开始,有一个特定的功能可以处理这种情况。 它被称为Portal
<块引用>门户提供了一种一流的方式来将子组件渲染到存在于父组件的 DOM 层次结构之外的 DOM 节点中。