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

React+TS视频录制组件异常:录制结束后Video仍显示直播流

React+TS视频录制后Video仍显示直播流的原因及解决方法

核心原因解析

1. React DOM节点复用导致属性冲突

如果你的条件渲染是基于同一个<video>组件切换class和源,却没有给不同状态下的video设置唯一key,React的diff算法会认为这是同一个DOM元素,只会更新部分属性(比如class),而不会重建节点。此时原有的srcObject(直播流)仍绑定在video元素上,而浏览器的video元素规则是:srcObject优先级高于src属性,所以即使你设置了录制后的blob URL,依然会显示旧的直播流。

2. 未主动清理MediaStream引用

即使你切换了src属性,只要video元素的srcObject没有被显式置为null,它会一直保持对直播流的引用,浏览器会优先加载这个流,忽略你设置的src URL。

解决方法

方法一:强制React重建Video节点

给录制状态和录制完成状态的video分别设置不同的key,让React销毁旧节点并创建新节点:

{isRecording ? (
  <video key="live-stream" className="LIVE" srcObject={mediaStream} autoPlay playsInline />
) : (
  <video key="recorded-video" className="RECORDED" src={recordingURL.current} autoPlay playsInline controls />
)}

方法二:手动清理直播流引用

如果不想重建DOM,可以通过ref获取video元素,在切换状态时先清空srcObject,再设置录制后的URL:

import { useRef, useEffect } from 'react';

// 组件内代码
const videoRef = useRef<HTMLVideoElement>(null);
const [isRecording, setIsRecording] = useState(true);
const recordingURL = useRef<string | null>(null);

useEffect(() => {
  if (!isRecording && videoRef.current && recordingURL.current) {
    // 先清理直播流引用
    videoRef.current.srcObject = null;
    // 再设置录制后的视频URL
    videoRef.current.src = recordingURL.current;
  }
}, [isRecording]);

// 渲染部分
<video 
  ref={videoRef}
  className={isRecording ? "LIVE" : "RECORDED"}
  autoPlay 
  playsInline 
  controls={!isRecording}
/>

方法三:确保录制URL触发组件重渲染

如果你用useRef存储recordingURL,注意ref的变化不会触发React重渲染。如果录制URL的更新晚于isRecording的状态切换,建议将recordingURL存入useState,确保状态更新时组件能同步渲染最新的src:

const [recordingURL, setRecordingURL] = useState<string | null>(null);

// 录制完成时更新state而非ref
const handleRecordingStop = async () => {
  const blob = await recorder.stop();
  const url = URL.createObjectURL(blob);
  setRecordingURL(url);
  setIsRecording(false);
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 12:20:30