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 行替换 <React.Fragment>...</React.Fragment>
<div>...</div>
片段可让您对子列表进行分组,而无需添加额外的节点 到 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 创建的代码沙盒现在已更改,但在原始代码沙盒中,片段包装视频播放器的位置。另外,如果我没记错的话,还有一种删除视频播放器的方法。视频播放器的父元素会尝试删除每个子元素,并且由于父元素是片段,这将失败,因为没有父子关系
这个答案帮助我找到了我的具体问题。就我而言,这个错误发生在我单独渲染/返回<i className="fa-circle"/>
时。这段代码解决了这个问题:<span><i className="fa-circle"/></span>
.
@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
。
【讨论】:
对我来说,您的解决方案结合使用<></>
而不是 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()
打开模式时,我会用一些新数据填充modalData
。 modalData.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 | MDNUncaught 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”上执行“定义”:此名称已用于此注册表