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

时间:2015-03-27 01:12:08

标签: javascript html5-video reactjs

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

它似乎完美但只适用于第一选择。当您从一个视频切换到另一个视频时(this.props.video更改时),实际显示并在页面中播放的视频不会更改。

我可以在Chrome检查器中看到<source src='blah.mp4' />元素更新,但页面中实际呈现的视频不会更改并继续播放(如果已经存在)。同样的事情发生在Safari&amp; 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;
})();

更新:

用另一种方式描述:

我有一个链接列表,其中包含设置组件道具的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>

然而它仍然是“视频Foo”,可见并且可以播放。

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

6 个答案:

答案 0 :(得分:8)

我已经描述了普通JavaScript here的一些方法。基于此,我找到了适合我的React解决方案:

  • 在视频中使用src属性:

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

    Dana's answer是扩展此解决方案的绝佳选择。

  • 在视频元素上使用.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>
        );
      }
    });
    

<强> UPD

  • 当然可以为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>
        );
      }
    });
    

答案 1 :(得分:3)

找到答案

  

当元素已插入视频或音频元素时动态修改源元素及其属性将不起作用。要更改正在播放的内容,只需直接在媒体元素上使用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;

})();

答案 2 :(得分:0)

我遇到了同样的问题,但是我无法访问<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>

这样,由于src道具会有所不同,因此React将呈现不同的视频标签,因此每次都会呈现不同的HTML标签,以避免出现此问题。

我发现这种方法更简洁,更简单,无论您是否有权访问<video> HTML标记,这两种方法都可以使用。

答案 3 :(得分:0)

尝试这种方式

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>
    );
  }
}

答案 4 :(得分:0)

尝试删除源标签,而只有视频标签,并像这个例子一样向其添加 src 属性

<video src={video} className="lazy" loop autoPlay muted playsInline poster="" />

答案 5 :(得分:-1)

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

所以我把视频播放器分成另一个反应组件 该组件收到两个道具:contentId(视频识别)&amp; videoUrl(视频网址)。

我还添加了一个参考视频标签,以便我可以管理标签。

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" />