ReactJS 组件中显示的视频未更新

IT技术 javascript html5-video reactjs
2021-04-08 20:59:10

我是 ReactJS (0.13.1) 的新手,我在我的应用程序中创建了一个组件来显示 HTML5 视频。

它似乎工作得很好,但仅适用于第一个选择。当您从一个视频切换到另一个视频时(this.props.video更改时),页面中实际显示和播放的视频不会更改

我可以<source src='blah.mp4' />在 Chrome 检查器中看到元素更新,但页面中实际呈现的视频不会改变,如果已经改变,它会继续播放。同样的事情发生在 Safari 和 Firefox 中。所有其他元素也相应地更新。

有任何想法吗?

无论如何我的组件如下:

(function(){
  var React = require('react');

  var VideoView = React.createClass({

    render: function(){
      var video = this.props.video;
      var title = video.title === ''? video.id : video.title;

      var sourceNodes = video.media.map(function(media){
        media = 'content/'+media;
        return ( <source src={media} /> )
      });
      var downloadNodes = video.media.map(function(media){
        var ext = media.split('.').slice(-1)[0].toUpperCase();
        media = 'content/'+media;
        return (<li><a className="greybutton" href={media}>{ext}</a></li>)
      });

      return (

        <div className="video-container">
          <video title={title} controls width="100%">
            {sourceNodes}
          </video>
          <h3 className="video-title">{title}</h3>
          <p>{video.description}</p>
            <div className="linkbox">
              <span>Downloads:</span>
              <ul className="downloadlinks">
                {downloadNodes}
              </ul>    
            </div>
        </div>

      )
    }
  });
  module.exports = VideoView;
})();

更新:

用另一种方式描述它:

我有一个带有设置组件props的 onClick 处理程序的链接列表。

当我第一次点击视频链接(“Video Foo”)时

<video title="Video Foo" controls>
  <source src="video-foo.mp4"/>
  <source src="video-foo.ogv"/>
</video>

并且出现“Video Foo”并且可以播放。

然后当我点击下一个(“视频栏”)时,DOM 更新,我得到

<video title="Video Bar" controls>
  <source src="video-bar.mp4"/>
  <source src="video-bar.ogv"/>
</video>

然而,它仍然是可见且可以播放的“Video Foo”。

就像一旦浏览器为 a 加载了媒体,<video>它就会忽略对<source>元素的任何更改

6个回答

在这里描述了一些纯 JavaScript 的方法基于此,我找到了适合我的 React 解决方案:

  • src在视频本身上使用属性:

    var Video = React.createComponent({
      render() {
        return <video src={this.props.videoUrl} />;
      }
    });
    

    Dana 的回答是扩展此解决方案的绝佳选择。

  • 使用.load()视频元素调用:

    var Video = React.createComponent({
      componentDidUpdate(_prevProps, _prevState) {
        React.findDOMNode(this.refs.video).load(); // you can add logic to check if sources have been changed
      },
    
      render() {
        return (
          <video ref="video">
            {this.props.sources.map(function (srcUrl, index) {
              return <source key={index} src={srcUrl} />;
            })}
          </video>
        );
      }
    });
    

更新

  • 当然,可以key<video>标签添加唯一属性(例如,基于您的来源),因此当来源发生变化时,它也会发生变化。但它会导致<video>完全重新渲染,并且可能会导致一些 UI 闪烁。

    var Video = React.createComponent({
      render() {
        return (
          <video key={this.props.videoId}>
            {this.props.sources.map(function (srcUrl, index) {
              return <source key={index} src={srcUrl} />;
            })}
          </video>
        );
      }
    });
    

找到答案

当元素已经插入到视频或音频元素中时,动态修改源元素及其属性将不起作用。要更改正在播放的内容,只需直接使用媒体元素上的 src 属性,可能会使用 canPlayType() 方法从可用资源中进行选择。通常,在解析文档后手动操作源元素是一种不必要的复杂方法

https://html.spec.whatwg.org/multipage/embedded-content.html#the-source-element

这是一个非常hacky和脆弱的,但它为我的案例完成了工作。

(function(){
  var React = require('react');

  var VideoView = React.createClass({

    pickSource: function(media){
      var vid = document.createElement('video');

      var maybes = media.filter(function(media){
        var ext = media.split('.').slice(-1)[0].toUpperCase();
        return (vid.canPlayType('video/'+ext) === 'maybe');
      });

      var probablies = media.filter(function(media){
        var ext = media.split('.').slice(-1)[0].toUpperCase();
        return (vid.canPlayType('video/'+ext) === 'probably');
      });

      var source = '';

      if (maybes.length > 0) { source = maybes[0]; }
      if (probablies.length > 0) { source = probablies[0]; }
      source = (source === '')? '' : 'content/'+source;
      return source;
    },

    render: function(){
      var video = this.props.video;
      var title = video.title === ''? video.id : video.title;

      var src = this.pickSource(video.media);

      var downloadNodes = video.media.map(function(media){
        var ext = media.split('.').slice(-1)[0].toUpperCase();
        media = 'content/'+media;
        return (
          <li><a className="greybutton" href={media}>{ext}</a></li>
        )
      });

      return (

        <div className="video-container">   
          <video title={title} src={src} controls width="100%"></video>
          <h3 className="video-title">{title}</h3>
          <p>{video.description}</p>
            <div className="linkbox">
              <span>Downloads:</span>
              <ul className="downloadlinks">
                {downloadNodes}
              </ul>
            </div>

        </div>

      )
    }
  });

  module.exports = VideoView;

})();

我遇到了同样的问题,我无法访问<video>HTML 标签,因为我使用库来呈现视频(不是本机<video>HTML 标签),它在内部负责呈现<video>标签。

在这种情况下,我找到了另一种解决方案,我认为它可以更好地解决相同的问题。

前:

<VideoLibrary src={this.props.src} />

后:

<React.Fragment key={this.props.src}>
  <VideoLibrary src={this.props.src} />
</React.Fragment>

或者,如果您使用本机<video>HTML 标记,则为

<React.Fragment key={this.props.src}>
  <video src={this.props.src} />
</React.Fragment>

这样 React 将渲染不同的视频标签,因为srcprop 会不同,因此每次渲染不同的 HTML 标签以避免这个问题。

我发现这种方式更清晰、更简单,并且无论您是否有权访问<video>HTML 标记,都可以在这两种情况下使用

我在制作带有视频的播放列表时遇到了同样的问题。

因此,我将视频播放器与另一个react组件分开,该组件收到两个props:contentId(视频标识)和 videoUrl(视频 URL)。

我还添加了对视频标签的引用,以便我可以管理标签。

var Video = React.createClass({
    componentWillReceiveProps (nextProps) {
        if (nextProps.contentId != this.props.contentId) {
            this.refs['videoPlayer'].firstChild.src = this.props.videoUrl;
            this.refs['videoPlayer'].load()
        }
    },
    propType: {
        contentId: React.PropTypes.string, // this is the id of the video so you can see if they equal, if not render the component with the new url 
        videoUrl: React.PropTypes.string, // the video url
    } ,
    getDefaultProps(){
        return {
        };
    },
    render() {
        return (
            <video ref="videoPlayer" id="video" width="752" height="423">
                <source src={this.props.videoUrl} type="video/mp4" />
            </video>
        );
    }
});

module.exports = Video;

这更干净:

<Video contentId="12" videoUrl="VIDEO_URL" />

试试这个方法

import React, { Component } from "react";

class Video extends Component<any, any> {

  video: any = React.createRef();

  componentDidUpdate(preProps: any) {
    const { url } = this.props;
    if (preProps && preProps.url && url) {
      if (preProps.url !== url) {
        this.video.current.src = url;
      }
    }
  }

  render() {
    const { url } = this.props;
    return (
      <video controls ref={this.video}>
        <source src={url} type="video/mp4" />
        Your browser does not support HTML5 video.
      </video>
    );
  }
}