You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React视频画廊播放报错:Cannot read properties of undefined (reading 'play')

视频画廊单次播放功能问题解决

问题概述

开发通过map渲染的视频画廊时,无法实现单次播放一个视频的功能,触发错误:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'play')

尝试将播放函数改为async/await后问题仍存在,该写法在单个视频场景下正常,但多视频场景失效。

核心错误原因

  1. 单个Ref无法绑定多个元素:原代码中使用单个useRef()绑定所有视频元素,map循环结束后,vidRef.current只会指向最后一个渲染的视频。点击其他视频的播放按钮时,无法获取对应视频的引用,调用play()时就会出现undefined错误。
  2. 全局状态导致控件失控:showControls是全局状态,一旦设置为true,所有视频都会显示控件,不符合“单次播放一个视频”的需求。
  3. 属性名错误:原代码中视频和播放按钮上写的是refs,正确的React ref属性名是ref,这也会导致ref无法正确绑定。

解决方案

1. 使用Ref数组存储每个视频引用

用useRef([])创建一个ref数组,在map循环中为每个视频元素分配对应的数组索引,确保能精准获取每个视频的DOM元素。

2. 维护独立的播放状态

用状态记录当前正在播放的视频索引,代替全局的showControls,实现单个视频的控件显示和播放状态控制。

3. 优化播放逻辑

播放指定视频时,先暂停其他正在播放的视频,再播放当前视频,确保同一时间只有一个视频在播放。

修改后的完整代码

export default function VideoGallery() {
  const [videoData, setVideoData] = useState([]);
  // 记录当前正在播放的视频索引,初始为null表示无视频播放
  const [playingIndex, setPlayingIndex] = useState(null);
  // 用ref数组存储每个视频元素的引用
  const vidRefs = useRef([]);

  // 播放指定索引的视频
  const playVideo = async (index) => {
    // 暂停之前正在播放的视频
    if (playingIndex !== null && vidRefs.current[playingIndex]) {
      await vidRefs.current[playingIndex].pause();
    }
    // 获取当前点击的视频元素
    const currentVideo = vidRefs.current[index];
    if (currentVideo) {
      await currentVideo.play();
      setPlayingIndex(index);
    }
  };

  // 视频播放结束后重置状态
  const handleVideoEnd = (index) => {
    if (playingIndex === index) {
      setPlayingIndex(null);
    }
  };

  return (
    <div className="relative w-full p-1 group">
      <div className="flex flex-wrap -m-1 md:-m-2">
        {videoData.map((data, index) => {
          const videoURL = data.videoURL.replace(
            "https://firebasestorage.googleapis.com",
            ""
          );
          return (
            <div
              key={data.videoURL}
              className="relative flex w-full md:w-1/3 sm:w-1/2 flex-wrap group"
            >
              <div className="relative w-full p-1 group">
                <video
                  src={videoURL}
                  preload="metadata"
                  volume={1}
                  // 仅当前播放的视频显示控件
                  controls={playingIndex === index}
                  controlsList="nodownload"
                  // 将视频元素存入ref数组对应位置
                  ref={(el) => (vidRefs.current[index] = el)}
                  onEnded={() => handleVideoEnd(index)}
                />
                {/* 仅未播放的视频显示播放按钮 */}
                {playingIndex !== index && (
                  <div className="absolute top-1/2 left-1/2 -translate-y-1/2 -translate-x-1/2">
                    <BsPlayCircle
                      className="text-[#fa9204] text-6xl cursor-pointer"
                      onClick={() => playVideo(index)}
                    />
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

关键修改点说明

  • 将单个vidRef改为vidRefs数组,每个视频对应数组中的一个索引,确保能准确获取目标视频元素。
  • 用playingIndex状态替代全局的showControls,精准控制单个视频的控件显示和播放状态。
  • 播放函数接收索引参数,先暂停之前的视频再播放当前视频,实现“单次播放一个视频”的需求。
  • 修正了原代码中错误的refs属性,改为正确的ref属性,确保ref绑定生效。
  • 添加onEnded事件监听,视频播放结束后自动重置播放状态,恢复播放按钮显示。

内容的提问来源于stack exchange,提问作者Coolkid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 20:38:17