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

Video displayed in ReactJS component not updating

本文关键字:更新 的视频 显示 组件 ReactJS      更新时间:2023-09-26

我是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>

然而,它仍然是"视频Foo"是可见的,可以播放。

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

我在这里描述了一些纯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>
        );
      }
    });
    

UPD

  • 当然,可以为<video>标记添加唯一的key属性(例如,基于您的源),所以当源发生更改时,它也会发生更改。但这将导致<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>
        );
      }
    });
    

我遇到了同样的问题,我无法访问<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将呈现不同的视频标签,因为src道具将不同,因此每次都会呈现不同的HTML标签以避免此问题。

我发现这种方法更干净、更简单,如果您有权或没有权访问<video>HTML标记,则在这两种情况下都可以使用。

找到答案

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

https://html.spec.whatwg.org/multipage/embedded-content.html#the-源元素

这是一个相当粗糙和脆弱的,但它完成了我的案件。

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

尝试这种方式

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

多亏了这家伙的博客,我的类似问题只需在视频标签中添加一个关键属性就解决了。

<video key={displayItem.key} controls>
    <source src={`${Config.MEDIA_URL}${displayItem.awsfile}`} />
</video>

公认的答案可能已经过时了?

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

所以我将视频播放器分离到另一个react组件并且该组件接收到两个道具:contentId(视频标识)&videoUrl(视频URL)。

此外,我在视频标签中添加了一个ref,这样我就可以管理标签了。

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

尝试删除源标签,而只使用视频标签并添加src属性,如以下示例

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

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

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