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

React组件中为HTML5 Video标签添加ended事件报错排查

解决React视频组件中addEventListener报错的问题

看起来你遇到的核心问题是:在componentDidMount里尝试获取视频元素的ref时,这个元素还没被渲染到DOM中,所以this.refs.introVideo返回undefined,调用addEventListener自然会报错。

为什么会出现这个问题?

你的视频元素被包裹在Dialog组件里,而Dialog的显示由this.props.isOpen控制。如果组件挂载时isOpen是false,那么Dialog及其内部的video元素根本不会被渲染,此时ref自然拿不到对应的DOM节点。另外,你使用的是React官方已经废弃的字符串ref,这种方式不仅不被推荐,也更容易出现无法准确获取元素的问题。

修复方案

这里给你两种可行的解决办法,你可以根据需求选择:

方法1:改用React.createRef()(推荐)+ 在componentDidUpdate中监听状态变化

先替换旧的字符串ref,使用类组件推荐的createRef,然后在组件更新时检查isOpen的状态变化,确保视频元素存在后再添加事件监听:

import React from 'react';

export default class ShowMovieWindow extends React.Component {
  constructor(props) {
    super(props);
    this.handleVideoEnd = this.handleVideoEnd.bind(this);
    // 创建ref实例
    this.introVideoRef = React.createRef();
  };

  handleVideoEnd() {
    console.log("Finished");
    // 在这里执行隐藏视频或其他后续操作
  }

  componentDidMount() {
    // 组件挂载时如果isOpen已经为true,直接添加监听
    if (this.props.isOpen && this.introVideoRef.current) {
      this.introVideoRef.current.addEventListener("ended", this.handleVideoEnd);
    }
  }

  componentDidUpdate(prevProps) {
    // 当isOpen从false变为true时,添加事件监听
    if (!prevProps.isOpen && this.props.isOpen && this.introVideoRef.current) {
      this.introVideoRef.current.addEventListener("ended", this.handleVideoEnd);
    }
    // 当isOpen从true变为false时,移除监听(避免内存泄漏)
    if (prevProps.isOpen && !this.props.isOpen && this.introVideoRef.current) {
      this.introVideoRef.current.removeEventListener("ended", this.handleVideoEnd);
    }
  }

  componentWillUnmount() {
    // 组件卸载前务必移除监听,防止内存泄漏
    if (this.introVideoRef.current) {
      this.introVideoRef.current.removeEventListener("ended", this.handleVideoEnd);
    }
  }

  render() {
    return (
      <Dialog isOpen={this.props.isOpen}>
        <div className={Classes.DIALOG_BODY}>
          <div>
            {/* 使用createRef绑定视频元素 */}
            <video ref={this.introVideoRef} autoPlay>
              <source src={'./asset/video/intro.mp4'} type="video/mp4"/>
            </video>
          </div>
        </div>
      </Dialog>
    );
  }
}

方法2:利用Dialog的onEntered回调(如果你的Dialog组件支持)

很多UI库的Dialog组件(比如Material-UI的Dialog)都提供了onEntered回调,这个回调会在Dialog的进入动画完成、组件完全渲染到DOM后触发,此时视频元素肯定已经存在,直接在这里添加监听即可:

import React from 'react';

export default class ShowMovieWindow extends React.Component {
  constructor(props) {
    super(props);
    this.handleVideoEnd = this.handleVideoEnd.bind(this);
    this.introVideoRef = React.createRef();
  };

  handleVideoEnd() {
    console.log("Finished");
  }

  // Dialog完全渲染完成后触发
  handleDialogEntered = () => {
    if (this.introVideoRef.current) {
      this.introVideoRef.current.addEventListener("ended", this.handleVideoEnd);
    }
  }

  // Dialog关闭动画完成后触发,移除监听
  handleDialogExited = () => {
    if (this.introVideoRef.current) {
      this.introVideoRef.current.removeEventListener("ended", this.handleVideoEnd);
    }
  }

  componentWillUnmount() {
    if (this.introVideoRef.current) {
      this.introVideoRef.current.removeEventListener("ended", this.handleVideoEnd);
    }
  }

  render() {
    return (
      <Dialog 
        isOpen={this.props.isOpen}
        onEntered={this.handleDialogEntered}
        onExited={this.handleDialogExited}
      >
        <div className={Classes.DIALOG_BODY}>
          <div>
            <video ref={this.introVideoRef} autoPlay>
              <source src={'./asset/video/intro.mp4'} type="video/mp4"/>
            </video>
          </div>
        </div>
      </Dialog>
    );
  }
}

额外注意事项

  • 一定要在组件卸载或者Dialog关闭时移除事件监听,避免内存泄漏。
  • 尽量不要再使用字符串ref,React官方已明确表示该方式会在未来版本中移除,推荐使用createRef(类组件)或useRef(函数组件)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:06:03