React中更新State时如何阻止Video.js播放器重新渲染
解决React状态更新导致Video.js播放器重置的问题
问题原因
videoJsOptions引用频繁变化:App组件每次渲染都会生成新的videoJsOptions对象,导致VideoJS子组件的optionsprop触发更新,进而执行内部逻辑重新设置视频源,让播放器从头播放。- 回调函数引用不稳定:
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
相关产品推荐
相关产品推荐

