简体   繁体   English

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

[英]Video displayed in ReactJS component not updating

I'm new to ReactJS (0.13.1), and I've created a component in my app to display HTML5 video.我是 ReactJS (0.13.1) 的新手,我在我的应用程序中创建了一个组件来显示 HTML5 视频。

It seems to work perfectly but only for the first selection.它似乎完美无缺,但仅适用于第一个选择。 The video that is actually displayed and playing in the page doesn't change when you switch from one video to another (when this.props.video<\/code> changes).当您从一个视频切换到另一个视频时(当this.props.video<\/code>发生变化时),页面中实际显示和播放的视频不会改变。

I can see the <source src='blah.mp4' \/><\/code> elements update in the Chrome inspector but the actually rendered video in the page doesn't change and keeps playing if it was already.我可以在 Chrome 检查器中看到<source src='blah.mp4' \/><\/code>元素更新,但页面中实际渲染的视频并没有改变,如果已经改变,它会继续播放。 Same thing happens in Safari & Firefox.同样的事情发生在 Safari 和 Firefox 中。 All the other elements update appropriately as well.所有其他元素也会相应地更新。

Any ideas?有任何想法吗?

Anyway my component below:无论如何,我的组件如下:

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

I have described some approaches for plain JavaScript here .在这里描述了一些纯 JavaScript 的方法。 Based on that I have found solutions for React which work for me:基于此,我找到了适合我的 React 解决方案:

  • using src attribute on video itself:在视频本身上使用src属性:

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

    Dana's answer is a great option extending this solution. Dana 的回答是扩展此解决方案的绝佳选择。

  • using .load() call on video element:在视频元素上使用.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 :更新

  • of course it's possible to add unique key attribute for <video> tag (for example based on your sources), so when sources will change it will be changed as well.当然,可以为<video>标签添加唯一的key属性(例如根据您的来源),因此当来源发生变化时,它也会发生变化。 But it will cause <video> to be re-rendered completely and it may cause some UI flashes.但它会导致<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> ); } });

Found the answer找到答案

Dynamically modifying a source element and its attribute when the element is already inserted in a video or audio element will have no effect.当元素已经插入到视频或音频元素中时,动态修改源元素及其属性将不起作用。 To change what is playing, just use the src attribute on the media element directly, possibly making use of the canPlayType() method to pick from amongst available resources.要更改正在播放的内容,只需直接使用媒体元素上的 src 属性,可能会使用 canPlayType() 方法从可用资源中进行选择。 Generally, manipulating source elements manually after the document has been parsed is an unnecessarily complicated approach通常,在解析文档后手动操作源元素是一种不必要的复杂方法

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

It's a pretty hacky and fragile, but it got the job done for my cases.这是一个非常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;

})();

I faced the same issue and I didn't have access to the <video> HTML tag as I was using a library to render the video (not the native <video> HTML tag) which is internally responsible for rendering the <video> tag.我遇到了同样的问题,我无法访问<video> HTML 标记,因为我使用库来呈现视频(不是本机<video> HTML 标记),它在内部负责呈现<video>标记.

In this case I have found another solution which I think is better to solve the same issue.在这种情况下,我找到了另一种解决方案,我认为它可以更好地解决相同的问题。

Before:前:

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

After:后:

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

Or this if you're using the native <video> HTML tag:或者,如果您使用本机<video> HTML 标签:

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

This way React will render different video tags because the src prop will be different hence rendering a different HTML tag each time to avoid this issue.这样 React 将渲染不同的视频标签,因为src属性会不同,因此每次渲染不同的 HTML 标签以避免这个问题。

I find this way cleaner and simpler and will work in both cases if you have or don't have access to the <video> HTML tag.我发现这种方式更清晰、更简单,并且无论您是否有权访问<video> HTML 标记,都可以在这两种情况下使用。

I had the same problem with making a playlist with videos.我在制作带有视频的播放列表时遇到了同样的问题。

So I separated the video player to another react component and that component received two props: contentId (video identify) & videoUrl (video URL).所以我将视频播放器与另一个反应组件分开,该组件收到两个道具:contentId(视频标识)和 videoUrl(视频 URL)。

Also I added a ref to the video tag so I can manage the tag.我还添加了对视频标签的引用,以便我可以管理标签。

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;

this is much more clean:这更干净:

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

Try this way试试这个方法

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

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

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

If you are getting the data from server and you want it to update the video link once you have new data.如果您从服务器获取数据,并且希望它在获得新数据后更新视频链接。

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

Based on evilguc's answer , I was able to fix a video update for my situation ( iframe instead of video tag).根据evilguc 的回答,我能够针对我的情况修复视频更新( iframe而不是video标签)。

In my case, the problem was with initial loading only.就我而言,问题仅在于初始加载。 It worked perfectly when the user changed the language manually, but with the initial load when language is detected with navigator.language , the src prop was not updated.当用户手动更改language时它工作得很好,但是当使用navigator.language检测到语言时的初始加载, src属性没有更新。

In case someone is struggling with Vimeo video and functional components, this is my implementation:如果有人在 Vimeo 视频和功能组件上苦苦挣扎,这是我的实现:

const videoURL = {
  en: "https://player.vimeo.com/video/{VIDEO-ID-EN}?h=88a8c88888&amp;badge=0&amp;autopause=0&amp;player_id=0&amp;app_id={YOUR-APP-ID}",
  ru: "https://player.vimeo.com/video/{VIDEO-ID-RU}?h=88b8d88888&amp;badge=0&amp;autopause=0&amp;player_id=0&amp;app_id={YOUR-APP-ID}",
};
    export const Video = ({ language }) => {
  const videoRef = useRef(null);

  useEffect(() => {
    ReactDOM.findDOMNode(videoRef.current).src = videoURL[language];
  });

  return (
    <div>
      <div style={{ padding: "56.25% 0 0 0", position: "relative" }}>
        <iframe
          ref={videoRef}
          src={videoURL[language]}
          frameBorder="0"
          allow="autoplay; fullscreen; picture-in-picture"
          style={{
            position: "absolute",
            top: 0,
            left: 0,
            width: "100%",
            height: "100%",
          }}
          title={i18n[language].videoTitle}
        ></iframe>
      </div>
      <script src="https://player.vimeo.com/api/player.js"></script>
    </div>
  );
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM