Портал React не может быть размонтирован без нереагирующего родителя DOM - как я могу размонтировать портал вручную перед удалением родителя?

У меня есть приложение-шаблон Blaze вне основного приложения React, в котором находится контейнер для портала React, который будет отображаться в нем. При рендеринге шаблона Blaze мы загружаем данные и отправляем настраиваемое событие в прослушиватель событий внутри приложения React, которое затем отображает портал.

Это прекрасно работает внутри Chrome, но с Internet Explorer 11 рендеринг и дендеринг портала и шаблона намного медленнее, и мы попадаем в состояние гонки?

При втором рендеринге шаблона 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. 
  }
}

Я попытался удалить отключение портала, отправив настраиваемое событие до того, как существующий код удалит контейнер 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. 
        })

Сейчас проблема в том, что unmountComponentAtNode, похоже, не работает для моего портала (который является классическим компонентом) - даже при передаче прямого родительского контейнера. Я подумал, что это может быть потому, что события не обязательно запускаются немедленно, и поэтому он может искать контейнер только после вызова .html (''), но это не проблема, после размещения события удаления портала сразу после событие создания портала (без удаления HTML между этими двумя вызовами).

Любая информация о том, как я могу аккуратно размонтировать и отключить портал до того, как его родительский контейнер будет заменен Blaze, будет чрезвычайно полезна!

Заранее спасибо.

Я ожидал по крайней мере успешного удаления портала в какой-то момент, но не сделал этого. Я не могу удалить вызов html. (''), Потому что именно так текущий шаблонизатор заменяет шаблоны.


person sqiii    schedule 27.08.2019    source источник


Ответы (1)


У меня была такая же проблема, когда мой компонент был удален из дерева DOM внешней структурой. В моем случае точка монтирования React была заменена на aspx UpdatePanel postBack. Чтобы исправить это, мне пришлось вынести свой компонент из Panel и смонтировать его через React.Portal.

Теперь все работает отлично, но кажется, что IE имеет немного другую реализацию функции removeChild - когда дочерний элемент не существует, выдается ошибка. поэтому мне пришлось глобально переоценить его, чтобы справиться с проблемой 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>
person Jakub Ojmucianski    schedule 24.01.2020