Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactJS can't change video and poster videojs

I want to change video poster and source VideoJS when selectedVideo changed

var VideoPlayer = React.createClass({
  render: function() {
    var selectedVideo = this.props.selectedVideo;
    function filterVideo(video) {
      return video.id == selectedVideo;
    }

    var data = this.props.videos.filter(filterVideo);
    return (
      <div className="col-md-6 videoplayer">
        <h2>{data[0].title}</h2>
        <video id="videoplayer" className="video-js vjs-default-skin vjs-big-play-centered" controls preload="none"
          width="100%"
          height="300"
          poster={data[0].poster}
          data-setup="{}">
          <source src={data[0].video} type='video/mp4' />
        </video>
        <div className="video-description">
          {data[0].description}
        </div>
      </div>
    );
  }
});

but i got error:

Uncaught Error: Invariant Violation: ReactMount: Two valid but unequal nodes with the same `data-reactid`: .0.0.1.1 

title and description changed but video poster doesn't change

like image 381
Andri Kurnia Avatar asked Oct 08 '14 11:10

Andri Kurnia


3 Answers

This is a problem with destructive libraries. Basically what happens is you render the <video> element, and then VideoJS injects a bunch of sibling elements next to your <video> (child elements are fine).

React tries to update the element, and it can't figure out what's going on, so it panics and gives that error.

So you have two options:

Option 1: render a <div ref="target" />, construct the video node in componentDidMount and this.refs.target.appendChild(that), and then invoke VideoJS manually. In componentWillRecieveProps you need to update the poster img's src directly.

Option 2: fork video.js and make it only emit events instead of directly modifying the DOM. Your component would react to those events, modifying state, and rendering the poster, buttons, etc.

Option 1 is easier, option 2 is potentially more efficient and cleaner. I'd go with option 1.


Neither of these are good solutions, but libraries that escape their container don't play nicely with react.

like image 119
Brigand Avatar answered Oct 06 '22 04:10

Brigand


In case someone needs an example of the answer from FakeRainBrigand, here's how my video class looks in the end.

Video = React.createClass({
  componentDidMount: function() {
    var video, wrapper;
    wrapper = document.createElement('div');
    wrapper.innerHTML = "<video id='attachmentVideo' class='video-js vjs-default-skin' controls preload='auto' width='640' height='264' poster='" + this.props.thumbnail + "'><source src='" + this.props.url + "' type='video/mp4' /><p className='vjs-no-js'>To view this video please enable JavaScript, and consider upgrading to a web browser that <a href='http://videojs.com/html5-video-support/' target='_blank'>supports HTML5 video</a></p></video>";
    video = wrapper.firstChild;
    this.refs.target.getDOMNode().appendChild(video);
    return videojs(video, {});
  },
  render: function() {
    return (
      <div id="attachmentViewer">
        <h2>{this.props.title}</h2>
        <div id="attachmentVideoContainer" ref="target" />
      </div>
    );
  }
});
like image 28
Ted Avery Avatar answered Oct 06 '22 03:10

Ted Avery


There is a question that answers this (I think it should work for your case let me know otherwise). You can change it over to React code from their example which is in jQuery by checking if the player has changed and then changing the src like so:

componentDidUpdate(prevProps) {
    if (prevProps.src !== this.props.src) { //see if the src changes
        if (this.initialized) { //check if the player has been previously initialized
            this.player.src({ //set the players new source
                src: this.props.src,
                type: 'video/mp4'
            })
        } else { 
            this.attemptInitialization() //only initialize player if it hasn't been
        }
    }
}
like image 24
swrap Avatar answered Oct 06 '22 04:10

swrap