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

React中更新State时如何阻止Video.js播放器重新渲染

解决React状态更新导致Video.js播放器重置的问题

问题原因

  • videoJsOptions引用频繁变化:App组件每次渲染都会生成新的videoJsOptions对象,导致VideoJS子组件的options prop触发更新,进而执行内部逻辑重新设置视频源,让播放器从头播放。
  • 回调函数引用不稳定:handlePlayerReady函数在App每次渲染时都会重新创建,作为prop传递给子组件可能引发不必要的重渲染。
  • 冗余的依赖项:VideoJS组件的useEffect将videoRef加入依赖列表,而React Ref的current值变化不会触发useEffect,这个依赖完全多余。

修复方案

1. 缓存视频配置对象

在App.js中用useMemo缓存videoJsOptions,确保只有当配置内容真正变化时才生成新对象。

2. 稳定回调函数引用

用useCallback包裹handlePlayerReady,避免每次渲染创建新函数实例。

3. 清理子组件的冗余依赖

移除VideoJS组件中useEffect的videoRef依赖项,同时补充必要的onReady依赖。


修改后的完整代码

App.js

import React, { useState, useRef, useMemo, useCallback } from 'react';
import videojs from 'video.js';
import VideoJS from './VideoJS';

const App = () => {
  const playerRef = useRef(null);
  const [timestamp, setTimestamp] = useState(0);

  // 用useMemo缓存配置对象,避免每次渲染生成新实例
  const videoJsOptions = useMemo(() => ({
    autoplay: true,
    controls: true,
    responsive: true,
    fluid: true,
    sources: [{
      src: '/path/to/video.mp4',
      type: 'video/mp4'
    }]
  }), []);

  // 用useCallback稳定回调函数引用
  const handlePlayerReady = useCallback((player) => {
    playerRef.current = player;

    player.on('waiting', () => {
      videojs.log('player is waiting');
    });

    player.on('dispose', () => {
      videojs.log('player will dispose');
    });

    player.on('timeupdate', function() {
      setTimestamp(player.currentTime());
    });
  }, []);

  return (
    <>
      <div>Rest of app here</div>
      <VideoJS options={videoJsOptions} onReady={handlePlayerReady} />
      <div>Rest of app here</div>
    </>
  );
};

export default App;

VideoJS.js

import React from 'react';
import videojs from 'video.js';
import 'video.js/dist/video-js.css';

export const VideoJS = (props) => {
  const videoRef = React.useRef(null);
  const playerRef = React.useRef(null);
  const { options, onReady } = props;

  React.useEffect(() => {
    // 确保播放器只初始化一次
    if (!playerRef.current) {
      const videoElement = document.createElement("video-js");
      videoElement.classList.add('vjs-big-play-centered');
      videoRef.current.appendChild(videoElement);

      const player = playerRef.current = videojs(videoElement, options, () => {
        videojs.log('player is ready');
        onReady && onReady(player);
      });
    } else {
      const player = playerRef.current;
      player.autoplay(options.autoplay);
      player.src(options.sources);
    }
  }, [options, onReady]); // 移除冗余的videoRef依赖,补充onReady依赖

  // 组件卸载时销毁播放器
  React.useEffect(() => {
    const player = playerRef.current;

    return () => {
      if (player && !player.isDisposed()) {
        player.dispose();
        playerRef.current = null;
      }
    };
  }, []);

  return (
    <div data-vjs-player>
      <div ref={videoRef} />
    </div>
  );
};

export default VideoJS;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 09:05:32