React:在自定义 React 视频播放器和 Video.js 之间切换导致“无法执行‘removeChild’”错误

问题描述 投票:0回答:1

我正在 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>

问题:

  1. 自定义播放器加载并运行良好。
  2. 切换到 Video.js 没有问题。
  3. 切换回自定义播放器时,出现错误无法在“节点”上执行“removeChild”:要删除的节点不是此节点的子节点

我尝试过的:

  • 我已确保在切换回自定义播放器之前处理掉 video.js 实例。
  • 我还重置了视频 src 并在视频元素上调用 load() 以确保它已为自定义播放器做好准备。
  • 我还尝试完全重新创建视频元素,但这导致 video.js 无法正确初始化。
reactjs html5-video video.js video-player
1个回答
0
投票

我找到了解决方案。该问题是由于我的视频播放器周围使用了片段 (<>)。由于 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 操作都会被正确清理。

© www.soinside.com 2019 - 2024. All rights reserved.