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
相关产品推荐
相关产品推荐

