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

Posted

技术标签:

【中文标题】ReactJS 组件中显示的视频未更新【英文标题】:Video displayed in ReactJS component not updating 【发布时间】: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 >
            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 是可选的,如果没有提供类型,浏览器会向提供文件的服务器查询。 【参考方案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 ></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"  >
                <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   controls >
                                        <source src=prevUploaded.url type="video/mp4" />
                                    </video>
        
                        </div>
                    </Suspense>
                    <);
    

【讨论】:

以上是关于ReactJS 组件中显示的视频未更新的主要内容,如果未能解决你的问题,请参考以下文章

视频更新ReactJs-第3节-组件&props

更新至 ReactJs-组件 & props | 先行者视频录像

ReactJS 中的路由。显示 URL 但未呈现组件

ReactJs 上下文未更新,值始终为默认值

调用 setState 后 ReactJS 元素未更新

关闭:ReactJS 控制的输入元素未更新