【问题标题】:React DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this nodeReact DOMException:无法在“节点”上执行“removeChild”:要删除的节点不是该节点的子节点
【发布时间】:2019-02-26 07:50:25
【问题描述】:

如果播放 youtube 视频的状态为 true,并且为 false,我想删除 youtube 播放。 我的代码如下。

{this.state.isPreViewVideo && <PlayYouTube video_id="ScSn235gQx0" />}

沙盒网址:

https://codesandbox.io/s/xryoz10k6o

复制方法:

如果输入表单中包含4位字符,则setState为“isPreViewVideo:true”,如果小于false

当状态为真时它工作正常, 但是当 state 为 false 时,我会遇到如下错误。

DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

有没有办法避免或解决这个错误?

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    在 playYouTube.tsx 第 78 行替换 &lt;React.Fragment&gt;...&lt;/React.Fragment&gt; &lt;div&gt;...&lt;/div&gt;

    片段可让您对子列表进行分组,而无需添加额外的节点 到 DOM。

    这解释了错误 Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

    更多关于片段在这里https://reactjs.org/docs/fragments.html

    【讨论】:

    • 您能否更详细地解释一下为什么会发生这种情况?为什么使用React.Fragment时会报错,而使用div时不会报错?
    • 是的,为什么会这样?如果没有任何可预见的原因发生这种情况,那么我们应该停止使用碎片吗??
    • 因为 Fragments 不会向 DOM 添加任何内容。请参阅由 React 团队 codepen.io/reactjs/pen/VrEbjE?editors=1000 创建的 Codepen 片段(>)未添加到 DOM。 @sukho 创建的代码沙盒现在已更改,但在原始代码沙盒中,片段包装视频播放器的位置。另外,如果我没记错的话,还有一种删除视频播放器的方法。视频播放器的父元素会尝试删除每个子元素,并且由于父元素是片段,这将失败,因为没有父子关系
    • 这个答案帮助我找到了我的具体问题。就我而言,这个错误发生在我单独渲染/返回&lt;i className="fa-circle"/&gt; 时。这段代码解决了这个问题:&lt;span&gt;&lt;i className="fa-circle"/&gt;&lt;/span&gt;.
    • @mojmir.novak 的评论对我很有帮助,因为它解决了这个问题。你能告诉我为什么它适用于 而不适用于 吗?
    【解决方案2】:

    如果使用 Google 翻译(或任何其他更改页面 DOM 的插件),也会引发此错误 Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node

    这在 Github 问题中有详细说明:https://github.com/facebook/react/issues/11538

    【讨论】:

      【解决方案3】:

      问题说明:

      isPreViewVideo 是真实的,你有这个:

      <PlayYouTube video_id="ScSn235gQx0" />
      

      这可能会呈现如下:

      <div> <!-- the root element as rendered by PlayYouTube if isPreViewVideo is truthy -->
        <embed /> <!-- the video player or whatever PlayYouTube renders -->
      </div>
      

      现在,如果某些代码通过直接操作 DOM 并删除根 div 来删除播放器,那么您最终会得到......好吧,什么都没有。

      但是在 React 的虚拟 DOM 中,根 div 仍然存在!因此,当isPreViewVideo 变为虚假时,React 会尝试将其从真实 DOM 中删除,但由于它已经消失,因此会引发错误。

      解决办法:

      扩展@henrik123 的答案 - 像这样用div 包裹PlayYouTube...

      <div> <!-- the root element rendered if isPreViewVideo is truthy -->
        <PlayYouTube video_id="ScSn235gQx0" />
      </div>
      

      ...导致渲染:

      <div> <!-- the root element rendered if isPreViewVideo is truthy -->
        <div> <!-- the element as rendered by PlayYouTube -->
          <embed /> <!-- the video player or whatever PlayYouTube renders -->
        </div>
      </div>
      

      现在,通过删除播放器的根 div 来删除播放器的相同代码使其看起来像这样:

      <div> <!-- the root element rendered if isPreViewVideo is truthy -->
      </div>
      

      现在当isPreViewVideo 变为假时,根在 React 的虚拟 DOM 和真实 DOM 中都存在,因此删除它没有问题。它的孩子已经改变,但 React 在这种情况下并不关心 - 它只需要删除根。

      注意:

      div 之外的其他 HTML 元素也可以使用。

      注2:

      React.Fragment 而不是div 包装是行不通的,因为React.Fragment 不会向真实的DOM 添加任何东西。所以有了这个...

      <React.Fragment>
        <PlayYouTube video_id="ScSn235gQx0" />
      </React.Fragment>
      

      ...你还是会得到这个:

      <div> <!-- the root element as rendered by PlayYouTube if isPreViewVideo is truthy -->
        <embed /> <!-- the video player or whatever PlayYouTube renders -->
      </div>
      

      你也有同样的问题。

      TL;DR 解决方案:

      div 包裹PlayYouTube

      【讨论】:

      • 对我来说,您的解决方案结合使用 &lt;&gt;&lt;/&gt; 而不是 div 将所有代码包装在 return 中解决了问题。 +1
      【解决方案4】:

      解决了手动关闭按钮后的引导警报

      当我手动关闭引导程序警报错误消息时,我遇到了确切的错误,然后再次提交表单。我所做的是用额外的标签包裹 AlertError 组件。

      import React from "react";
      
      const AlertError = (props) => {
          return (
              <div> // <---- Added this
                  <div className="alert alert-custom alert-notice alert-light-danger fade show" role="alert">
                      <div className="alert-icon"><i className="flaticon-warning"></i></div>
                      <div className="alert-text">There is an error in the form..!</div>
                      <div className="alert-close">
                          <button type="button" className="close" data-dismiss="alert" aria-label="Close">
                              <span aria-hidden="true"><i className="ki ki-close"></i></span>
                          </button>
                      </div>
                  </div>
              </div> // <---- Added this
          );
      };
      
      export default AlertError;
      

      【讨论】:

        【解决方案5】:

        当您使用外部 JS 或 jQuery 插件时,可能会出现此错误。说说我的情况吧。

        问题描述

        在我的 React 项目中,我在页面上有一些链接,每个链接都会打开一个带有一些信息的模式和一个带有图像的滑块。每个链接在滑块内都有自己的一组图像。但我有单一模式,每个链接都是一样的。 模态的数据存储在一个状态变量中,我会在每次链接点击时更改其内容(在每次打开模态时):

        const initialData = { title: "", intro: "" }; // here is some empty structure of object properties, we need it for the modalResponse initialization
        const [modalData, setModalData] = useState(initialData);
        // ...
        <div id="MyModal">
            <div className="title">{modalData.title}</div>
            <div className="intro">{modalData.intro}</div>
            <div>{modalData.sliderHtml}</div>
        </div>
        

        当我使用setModalData() 打开模式时,我会用一些新数据填充modalDatamodalData.sliderHtml 中的 HTML 代码类似于这样的结构:

        <div class="carousel">
            <div class="item"><img src="first.jpg" /></div>
            <div class="item"><img src="second.jpg" /></div>
            <div class="item"><img src="third.jpg" /></div>
        </div>
        

        当模态打开时,我调用一些 jQuery 代码来初始化滑块:

        useEffect(() => {
            $('.carousel').initCarousel({
                // some options
            });
        }, [modalData]);
        

        然后用户可以关闭模​​式并单击下一个链接。滑块必须由一组新图像填充。但我得到了错误:Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

        错误原因

        问题的原因在于 jQuery 插件在初始化期间改变了 html 的结构。 它在 .carousel 父级内创建了一些新块(向左/向右箭头,导航点等)。它会将所有.item 子代移动到新的.items 块内!插件初始化后得到这样的html结构:

        <div class="carousel">
            <div class="dots">...</div>
            <div class="arrows">...</div>
            <div class="items">
                <div class="item"><img src="first.jpg" /></div>
                <div class="item"><img src="second.jpg" /></div>
                <div class="item"><img src="third.jpg" /></div>
            </div>
        </div>
        

        当 React 尝试更改 modalData.sliderHtml 的内容时,它会执行一些神奇的 DOM 操作来移除旧结构。其中一个操作是removeChild 方法,但在父.carousel 中找不到旧的.item 元素,因为插件将它们移动到新的.items 元素中。所以,我们得到了错误。

        错误修复

        我开始在每次模态关闭时将modalData内容改回initialData结构。

        useEffect(() => {
            $('#MyModal').on('hidden.bs.modal', function (e) {
                setModalData(initialData);
            })
        }, []);
        

        所以,modalData.sliderHtml 中的所有 html 结构再次被初始的空数据填充,我们在新链接点击时不会收到错误。

        【讨论】:

          【解决方案6】:

          我在使用音频标签时遇到了同样的问题,我将这个标签保存在两个嵌套的 div 中,现在它可以工作了

          【讨论】:

            【解决方案7】:

            Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

            修复:

               unmountContainer() {
                  // macro task
                  setTimeout(() => {
                    // refs
                    if (containerEle) {
                      // receive removed node
                      // eslint-disable-next-line no-unused-vars
                      let removedChild = document
                        .querySelector('.page__wrapper')
                        .removeChild(containerEle);
                    }
                  });
                }
            

            【讨论】:

              【解决方案8】:

              我们的代码有一个地方是这样的:

              render() {
               if (el) return <div />
              
               return '';
              }
              

              将''替换为null后,一切正常。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2018-11-26
                • 2022-11-12
                • 2019-06-17
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-03-03
                • 1970-01-01
                相关资源
                最近更新 更多