React DOMException:无法在“节点”上执行“removeChild”:要删除的节点不是该节点的子节点

Posted

技术标签:

【中文标题】React DOMException:无法在“节点”上执行“removeChild”:要删除的节点不是该节点的子节点【英文标题】:React DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node 【发布时间】: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.

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

【问题讨论】:

【参考方案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】: Node.removeChild() - Web APIs | MDN

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后,一切正常。

【讨论】:

以上是关于React DOMException:无法在“节点”上执行“removeChild”:要删除的节点不是该节点的子节点的主要内容,如果未能解决你的问题,请参考以下文章

React Uncaught (in promise) DOMException: play() 请求被新的加载请求中断

未捕获的 DOMException:无法读取“cssRules”属性

WINDOWS 上的 WebSerial API - “DOMException:无法打开串行端口。”

未捕获的 DOMException:无法在“IDBObjectStore”上执行“删除”:事务未处于活动状态

未捕获的 DOMException:无法在“CustomElementRegistry”上执行“定义”:此名称已用于此注册表

IndexedDB:未捕获(承诺)DOMException