【问题标题】:Video displayed in ReactJS component not updatingReactJS 组件中显示的视频未更新
【发布时间】:2015-05-31 06:00:17
【问题描述】:

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

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

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

更新:

换一种说法:

我有一个带有设置组件道具的 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”。

就像一旦浏览器加载了&lt;video&gt; 的媒体,它就会忽略对&lt;source&gt; 元素的任何更改。

【问题讨论】:

  • type 是可选的,如果没有提供类型,浏览器会向提供文件的服务器查询。

标签: javascript html5-video reactjs


【解决方案1】:

我已经描述了一些纯 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

  • 当然可以为&lt;video&gt; 标签添加唯一的key 属性(例如基于您的来源),因此当来源发生变化时,它也会发生变化。但这会导致&lt;video&gt;被完全重新渲染,并可能导致一些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>
        );
      }
    });
    

【讨论】:

    【解决方案2】:

    找到答案

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

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

    这是一个相当老套和脆弱的,但它为我的案例完成了工作。

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

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题,我无法访问 &lt;video&gt; HTML 标记,因为我正在使用一个库来呈现视频(不是本机 &lt;video&gt; HTML 标记),它在内部负责呈现 @ 987654323@标签。

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

      之前:

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

      之后:

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

      或者如果你使用原生的&lt;video&gt; HTML 标签:

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

      这种方式 React 将渲染不同的视频标签,因为 src 属性会不同,因此每次渲染不同的 HTML 标签以避免这个问题。

      我发现这种方式更简洁,并且无论您是否有权访问&lt;video&gt; HTML 标记,都可以在这两种情况下使用。

      【讨论】:

      • 这对我们有用!
      【解决方案4】:

      试试这个方法

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

      【讨论】:

        【解决方案5】:

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

        所以我将视频播放器分离到另一个反应组件 并且该组件收到了两个道具: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" />
        

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

            【解决方案7】:

            如果您从服务器获取数据,并且希望它在获得新数据后更新视频链接。

                import React, {Fragment, useEffect, useState} from "react";
                const ChangeVID =(props)=> {
                 
                const [prevUploaded, setPrevUploaded] =useState(null);
                useEffect(()=>{
                if(props.changeIMG) {
                setPrevUploaded(props.changeIMG)}
            
            },[])
                return(<Fragment>
                        {prevUploaded && <Suspense
                                    fallback={<div>loading</div>}>
                                    <div className="py-2" >
                                        { <video id={prevUploaded.duration} width="320" height="240" controls >
                                                    <source src={prevUploaded.url} type="video/mp4" />
                                                </video>}
                    
                                    </div>
                                </Suspense>
                                }<);
                }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2016-08-25
              • 2017-08-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-10-13
              相关资源
              最近更新 更多