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

React函数组件中视频随重渲染重启问题及原因分析

解决useVideo Hook中视频组件因重渲染重启的问题

我之前遇到过这个问题:封装的useVideo Hook在App组件重渲染时,视频总会自动重启。先贴出当时的代码:

问题代码

useVideo Hook实现

function useVideo() {
  const videoRef = useRef(null);

  function playPause() {
    if (videoRef.current.paused) {
      videoRef.current.play();
    } else {
      videoRef.current.pause();
    }
  }

  function Video() {
    return <video ref={ videoRef } ...etc />
  }

  return { playPause, Video };
}

App组件使用

function App() {
  const { playPause, Video } = useVideo();

  ...

  return <Video key="key" />;
}

问题原因

每次App重渲染都会调用useVideo,而Hook内部的Video组件函数会被重新创建。React会把新的Video函数视为完全不同的组件,哪怕加了key也没用——它会卸载旧的视频DOM节点,重新挂载新的,自然就导致视频重启了。

解决方案

方案1:将Video组件抽离为独立组件

把Video组件从Hook里移出来,通过props传递ref和其他属性,这样组件引用不会随Hook调用而变化:

// 独立的Video组件
function Video({ videoRef, ...props }) {
  return <video ref={videoRef} {...props} />;
}

function useVideo() {
  const videoRef = useRef(null);

  function playPause() {
    // 加可选链避免null报错
    if (videoRef.current?.paused) {
      videoRef.current.play();
    } else {
      videoRef.current?.pause();
    }
  }

  return { playPause, videoRef };
}

App组件里这样用:

function App() {
  const { playPause, videoRef } = useVideo();

  return <Video videoRef={videoRef} />;
}

方案2:用useMemo缓存Hook内的Video组件

如果一定要在Hook里定义组件,可以用useMemo缓存组件函数,确保每次Hook返回的都是同一个引用:

function useVideo() {
  const videoRef = useRef(null);

  function playPause() {
    if (videoRef.current?.paused) {
      videoRef.current.play();
    } else {
      videoRef.current?.pause();
    }
  }

  // 用useMemo缓存组件,依赖为空数组保证引用不变
  const Video = useMemo(() => {
    return function VideoComponent({ ...props }) {
      return <video ref={videoRef} {...props} />;
    };
  }, []);

  return { playPause, Video };
}

这样React会识别到Video是同一个组件,不会重复卸载挂载,视频状态就能保留下来了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 01:52:41