8

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

Thomas
  • 6,291
  • 6
  • 40
  • 69
Andri Kurnia
  • 2,148
  • 1
  • 13
  • 13

5 Answers5

16

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.

Brigand
  • 84,529
  • 20
  • 165
  • 173
  • @fakerainbrigand can you provide an example? Specifically in regards to componentWillReceiveProps and cleanup? https://github.com/videojs/video.js/issues/2006 – chemoish Sep 11 '15 at 03:13
  • For me, it's the ` – paddotk Mar 30 '17 at 15:28
11

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>
    );
  }
});
Ted Avery
  • 5,639
  • 3
  • 30
  • 29
1

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
        }
    }
}
swrap
  • 314
  • 1
  • 3
  • 11
0

Not 100% related, but if you're looking for a more "pure" react solution to implementing videoJS as a component, this is what I did...

The last post was a while ago and I don't know if anyone came up with a better implementation, however, what I did was the following:

  1. Added the videojs function as a getInitialState prop and added a "videoHTML" prop to the getInitialState as well.
  2. Generated my video tag and sources in the componentDidMount method and assigned that variable string to the videoHTML state prop.
  3. Added a dispose() of the instantiated vjs player and a replaceState of the initial state to the componentWillUnmount() method.
  4. Added a component function __initVideo(elem) that checks to see if the passed in element has children and if it does then "this.vjsPlayer = this.state.initVideoJS(elem.children[0], {})". Then I return this.vjsPlayer.
  5. In the attachment div in the render() method, I set the dangerouslySetInnerHTML prop to the this.state.videoHTML prop and set a ref that calls the the __initVideo function, passing in the div.

Can't post any code, but this is as close to a pure React implementation of videoJS that I've been able to figure out.

RickZ
  • 141
  • 1
  • 12
0

1.import

import ReactDOM from 'react-dom'

2.inside function

this.setstate({vidio:"{new url as a source}") var name = ReactDOM.findDOMNode(this.video) name.src=this.state.video.url 

3.Template

<VideoPlayer ref={video => this.video = video}src={this.state.video.url}
                       />