我正在 React 中开发一个视频播放器组件,我使用切换按钮在自定义视频播放器和 Video.js 之间切换。自定义播放器的初始加载工作正常,当我切换到 Video.js 时,它也工作得很好。但是,当我尝试切换回自定义播放器时,我收到以下错误:
未捕获的运行时错误: 无法在“Node”上执行“removeChild”:要删除的节点不是该节点的子节点。
这是我的代码的相关部分:
const videoRef = useRef(null);
const canvasRef = useRef(null);
const playerRef = useRef(null);
const [isCustomPlayer, setIsCustomPlayer] = useState(true);
const handleTogglePlayer = () => {
setIsCustomPlayer((prev) => !prev);
};
useEffect(() => {
if (videoRef.current) {
if (isCustomPlayer) {
if (playerRef.current) {
playerRef.current.dispose(); // Disposing Video.js instance
playerRef.current = null; // Resetting the reference
}
videoRef.current.src = videoData?.video_link || '';
} else {
if (!playerRef.current) {
playerRef.current = videojs(videoRef.current, {
controls: true,
autoplay: false,
preload: "auto",
sources: [{
src: videoData?.video_link,
type: 'video/mp4'
}]
});
}
}
}
return () => {
if (playerRef.current) {
playerRef.current.dispose();
playerRef.current = null;
}
};
}, [isCustomPlayer, videoData]);
<div style={{ position: "relative" }}>
{isCustomPlayer ? (
// Custom player
<video
key="custom-player"
id="video-player"
className={
isShortVideo ? "short-video-container" : "video-container"
}
ref={videoRef}
src={videoData?.video_link}
controls
controlslist="nodownload"
disablePictureInPicture
></video>
) : (
// Video.js player
<video
key="video-js-player"
id="video-player"
ref={videoRef}
className={`video-js vjs-default-skin vjs-big-play-centered ${
isShortVideo ? "short-video-container" : "video-container"
}`}
controls
></video>
)}
<canvas ref={canvasRef} style={{ display: "none" }} />
className="btn btn-toggle-player"
onClick={handleTogglePlayer}
>
Switch to {isCustomPlayer ? "Video.js" : "Custom"} Player
</button>
问题:
我尝试过的:
我找到了解决方案。该问题是由于我的视频播放器周围使用了片段 (<>)。由于 Fragment 不会创建实际的 DOM 元素,因此在 Video.js 和自定义播放器之间切换时,React 无法正确管理 DOM,尤其是 Video.js 操作 DOM 的方式。
我用每个玩家周围的 div 包装器替换了 Fragment。这为 React 提供了适当的 DOM 元素来管理,并允许玩家之间的切换顺利进行。
<div style={{ position: "relative" }}>
{isCustomPlayer ? (
<div key="custom-player">
<video
id="video-player"
ref={videoRef}
className={isShortVideo ? "short-video-container" : "video-container"}
src={videoData?.video_link}
controls
controlslist="nodownload"
disablePictureInPicture
></video>
</div>
) : (
<div key="video-js-player">
<video
id="video-player"
ref={videoRef}
className={`video-js vjs-default-skin vjs-big-play-centered ${
isShortVideo ? "short-video-container" : "video-container"
}`}
controls
></video>
</div>
)}
</div>
为什么有效:
切换到 div 而不是 Fragment 会在每个玩家周围创建一个适当的 DOM 包装器,使 React 能够更有效地处理安装和卸载过程。这确保了切换回自定义播放器时,Video.js 完成的任何 DOM 操作都会被正确清理。