简体   繁体   English

如果没有非 React DOM 父级,则无法卸载 React 门户 - 如何在删除父级之前手动卸载门户?

[英]React portal cannot be unmounted without non-react DOM parent - how can I unmount the portal manually before removing the parent?

I have a Blaze template application outside of the main react app that houses a container for a React portal to be rendered within it.我在主 React 应用程序之外有一个 Blaze 模板应用程序,其中包含一个用于在其中呈现的 React 门户的容器。 On render of the Blaze template, we load data and send a custom event to an event listener inside the React application, which then renders the portal.在渲染 Blaze 模板时,我们加载数据并将自定义事件发送到 React 应用程序内的事件侦听器,然后由它渲染门户。

This works perfectly inside Chrome, but with Internet Explorer 11, the rendering and derendering of both the portal and template is a lot slower, and we run into a race condition??这在 Chrome 中完美运行,但使用 Internet Explorer 11,门户和模板的渲染和反渲染都慢得多,并且我们遇到了竞争条件??

On the second render of the Blaze template, React goes through its lifecycle and attempts to unmount all the children of the previous portal container, however - they do not exist since we've removed that DOM node entirely (using .html('')), and we run into an error within ReactDOM where the child is not found.在 Blaze 模板的第二次渲染中,React 经历了它的生命周期并尝试卸载前一个门户容器的所有子级,然而 - 它们不存在,因为我们已经完全删除了那个 DOM 节点(使用 .html('') ),我们在 ReactDOM 中遇到了一个错误,其中没有找到孩子。

function removeChildFromContainer(container, child) {
  if (container.nodeType === COMMENT_NODE) {
    container.parentNode.removeChild(child);
  } else {
    container.removeChild(child); // Error on this line. 
  }
}

I've attempted to remove unmount the portal by sending a custom event before the existing code removes the DOM container.我试图通过在现有代码删除 DOM 容器之前发送自定义事件来删除卸载门户。

    //Derender the previous portal if it exists
    if (window.isEvalTool) {
        console.log('removing the portal')
        // Send an event to React to unmount the previous portal node for IE
        var container = document.getElementById('eval-panel-root');
        var removePortal = new CustomEvent("readyForPortalRemoval", {
            detail: container
        });
        window.dispatchEvent(removePortal);
    }
    $(findingContainer).html('');


// Code that should unmount the component manually
        window.addEventListener('readyForPortalRemoval', function (event) {
            console.log('removing previous portal container');
            var removed = ReactDOM.unmountComponentAtNode(event.detail);
            console.log(removed); // This usually returns false, meaning it 
            // couldn't find it. 
        })

Right now the problem is that unmountComponentAtNode doesn't seem to work for my portal (which is a classical component) - even when passing the direct parent container.现在的问题是 unmountComponentAtNode 似乎不适用于我的门户(这是一个经典组件) - 即使在传递直接父容器时也是如此。 I thought it might be because events aren't guaranteed to fire immediately and so it might be looking for the container only after the .html('') call, but that isn't the issue, after placing the remove portal event right after the create portal event (no removal of html between those two calls).我认为这可能是因为事件不能保证立即触发,所以它可能只在 .html('') 调用之后寻找容器,但这不是问题,在之后放置删除门户事件之后创建门户事件(在这两个调用之间没有删除 html)。

Any information on how I can gracefully unmount and derender the portal before its parent container gets replaced by Blaze would be extremely helpful!关于如何在门户的父容器被 Blaze 替换之前优雅地卸载和取消渲染门户的任何信息都将非常有帮助!

Thanks in advance.提前致谢。

I expected to at least have a successful removal of the portal at some point but have not.我希望至少在某个时候成功移除门户,但没有。 I cannot remove the html.('') call because that is how the current templating engine replaces templates.我无法删除 html.('') 调用,因为这是当前模板引擎替换模板的方式。

I had the same issue when my component was removed from DOM tree by an external framework.当我的组件被外部框架从 DOM 树中删除时,我遇到了同样的问题。 In my case React mounting point has been replaced by aspx UpdatePanel postBack.在我的情况下,React 安装点已被 aspx UpdatePanel postBack 替换。 In order to fix it, I had to move my component out of the Panel and mount it through React.Portal.为了修复它,我不得不将我的组件移出面板并通过 React.Portal 安装它。

Everything now works perfectly fine but it seems that IE has a little different implementation of removeChild function - when the child doesn't exist an error is thrown.现在一切正常,但似乎 IE 对 removeChild 函数的实现略有不同——当孩子不存在时,会抛出错误。 so I had to globally overite it to handle "NotFoundError" issue.所以我不得不全局覆盖它来处理“NotFoundError”问题。

   <script type="text/javascript">
          function isIE() {
              ua = navigator.userAgent;
              /* MSIE used to detect old browsers and Trident used to newer ones*/
              var is_ie = ua.indexOf("MSIE ") > -1 || ua.indexOf("Trident/") > -1;

              return is_ie;
          }
          if (isIE()) {
              //Dev note: IE only - React JS throws NotFoundError when removeChildFromContainer inside of React DOM liv and child container doens't contain coresponding node. 
              let oldRemoveChild = HTMLElement.prototype.removeChild;
              HTMLElement.prototype.removeChild = function () {
                  try {
                          oldRemoveChild.apply(this, arguments);
                  }
                  catch (error) {

                      if (!!error && !!error.message && error.message === "NotFoundError") {
                          console.warn("IE NotFoundError handled")
                      }
                      else {
                          throw error;
                      }
                  }
              }
          }
    </script>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM