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

Posted

技术标签:

【中文标题】管理不在 DOM 子树中的子组件【英文标题】:Managing subcomponents that are not in DOM subtree 【发布时间】:2014-03-31 21:33:00 【问题描述】:

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

有没有更好、更惯用的方式?

【问题讨论】:

Pete Hunt 提出了一个 ReactLayeredComponentMixin ,它将类似于 Float 的 Modal 移动到 Mixin 中,并在组件上公开了 renderLayer 函数,使用它让组件有效地渲染到两个单独的容器:jsfiddle.net/LBAr8 因为它是一个 Mixin 并且使用生命周期方法,你可能会称之为更地道。 【参考方案1】:

只是这个问题的访问者的提示。

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

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

【讨论】:

以上是关于管理不在 DOM 子树中的子组件的主要内容,如果未能解决你的问题,请参考以下文章

每日思考(2020/01/03)

videojs文档翻译-Component

有没有办法用它的内容替换我在 dom 中的组件?

挂载方法中的 DOM 修改不在 JEST 快照中

React:子组件在提交表单时不会重新呈现

Vue父组件如何调用子组件(弹出框)中的方法的问题