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
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.
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>
);
}
});
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
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With