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

2021-06-22 16:37:11 +08:00
 buddie
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 这个操作呢?谢谢鸭!

1488 次点击
所在节点    React
7 条回复
duduaba
2021-06-22 16:49:18 +08:00
拆分为视频、时间两个组件,然后用组件传值的形式更新 currentTime 组件,这样只有时间组件会更新了。或者简单点,用 ref 吧,或者再简单点,document.querySelector()的形式更新。
buddie
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
2021-06-22 17:03:31 +08:00
好了!我把拆开的 Video 组建用 React.memo 包住就可以了!谢谢!
liyang5945
2021-06-22 17:05:36 +08:00
不能用 onTimeUpdate 这个事件,这个事件只要播放就会一直触发,就会一直重绘,给你个思路:监听 play 或 pause 事件,在监听方法里用定时器获取视频 refs 的当前播放时间
sweetcola
2021-06-22 17:15:34 +08:00
这个问题主要是 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
2021-06-22 17:25:29 +08:00
setCurrentTime 之后 App rerender 然后每次 createObjectURL 都会生成一个新的 URL 对象,然后 video 又 rerender 了

video 依赖的 src 应该挪到一个 useMemo 里面算出来
coolzjy
2021-06-22 18:59:02 +08:00
@sweetcola 正解

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/785112

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX