管理不在DOM子树中的子组件

时间:2014-02-28 03:41:01

标签: javascript reactjs

考虑一个组件,它需要在自己的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()在组件仍在使用时卸载该组件。这很难看,虽然它确实有效。

是否有更好,更惯用的方式?

1 个答案:

答案 0 :(得分:0)

只是给这个问题的访问者提个醒。

从 React v16 开始,有一个特定的功能可以处理这种情况。 它被称为Portal

<块引用>

门户提供了一种一流的方式来将子组件渲染到存在于父组件的 DOM 层次结构之外的 DOM 节点中。