V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
buddie
V2EX  ›  React

一个 React re-render 导致 html5 视频无法播放的问题

  •  
  •   buddie · 2021-06-22 16:37:11 +08:00 · 1464 次点击
    这是一个创建于 1010 天前的主题,其中的信息可能已经有所发展或是发生改变。
    import React, { useState } from 'react';
    
    export default function App() {
      const [video, setVideo] = useState();
      const [currentTime, setCurrentTime] = useState(0);
    
      return (
        <div className="App">
          {video && (
            <video
              controls
              onTimeUpdate={(e) => setCurrentTime(e.target.currentTime)}
              src={URL.createObjectURL(video)}
              width="250"
            />
          )}
          <p>{currentTime}</p>
          <input type="file" onChange={(e) => setVideo(e.target.files?.item(0))} />
        </div>
      );
    }
    

    一个很简单的 App,加载本地视频,在视频播放的同时,在页面上显示当前视频的时间。但是载入视频后,点击播放按钮,视频无法播放,看起来像是被重绘了,是因为 setCurrentTime 更新了状态数据,从而导致的重绘吗?那我能在哪里做 setCurrentTime 这个操作呢?谢谢鸭!

    7 条回复    2021-06-22 18:59:02 +08:00
    duduaba
        1
    duduaba  
       2021-06-22 16:49:18 +08:00   ❤️ 1
    拆分为视频、时间两个组件,然后用组件传值的形式更新 currentTime 组件,这样只有时间组件会更新了。或者简单点,用 ref 吧,或者再简单点,document.querySelector()的形式更新。
    buddie
        2
    buddie  
    OP
       2021-06-22 16:58:21 +08:00
    @coderfuns 我将他们拆开了还是一样问题...

    ```jsx
    import React, { useState } from 'react';

    function CurrentTime({ currentTime }) {
    return <p>{currentTime}</p>;
    }

    function Player({ video, onTimeUpdate }) {
    return (
    <video
    controls
    onTimeUpdate={(e) => onTimeUpdate(e.target.currentTime)}
    src={URL.createObjectURL(video)}
    width="250"
    />
    );
    }

    export default function App() {
    const [video, setVideo] = useState();
    const [currentTime, setCurrentTime] = useState();

    return (
    <>
    <input type="file" onChange={(e) => setVideo(e.target.files?.item(0))} />
    {video && <Player video={video} onTimeUpdate={setCurrentTime} />}
    <CurrentTime currentTime={currentTime} />
    </>
    );
    }

    ```
    buddie
        3
    buddie  
    OP
       2021-06-22 17:03:31 +08:00
    好了!我把拆开的 Video 组建用 React.memo 包住就可以了!谢谢!
    liyang5945
        4
    liyang5945  
       2021-06-22 17:05:36 +08:00   ❤️ 1
    不能用 onTimeUpdate 这个事件,这个事件只要播放就会一直触发,就会一直重绘,给你个思路:监听 play 或 pause 事件,在监听方法里用定时器获取视频 refs 的当前播放时间
    sweetcola
        5
    sweetcola  
       2021-06-22 17:15:34 +08:00   ❤️ 4
    这个问题主要是 URL.createObjectURL(video) 这一段。

    触发 re-render 后,React 会重新计算所有的值,包括 src={URL.createObjectURL(video)} 这一段。所以每当 re-render 后都会看上去停止了,实际上是因为 src 被重新计算了。

    解法可以像你 3L 说的那样,也可以在不改动原来代码的基础上用 useMemo 。

    ```
    const objURL = useMemo(() => {
    return video ? URL.createObjectURL(video) : undefined
    }, [video])

    ...

    src={objURL}
    ```
    auroraccc
        6
    auroraccc  
       2021-06-22 17:25:29 +08:00   ❤️ 1
    setCurrentTime 之后 App rerender 然后每次 createObjectURL 都会生成一个新的 URL 对象,然后 video 又 rerender 了

    video 依赖的 src 应该挪到一个 useMemo 里面算出来
    coolzjy
        7
    coolzjy  
       2021-06-22 18:59:02 +08:00
    @sweetcola 正解
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2712 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 15:33 · PVG 23:33 · LAX 08:33 · JFK 11:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.