Donny P Donny P - 4 months ago 50
Javascript Question

Update React component when state changes, using a reference to itself

In a React component, I define how the component handles state change in a callback to

setState()
. This seems wrong / against flux convention. Where is the correct place to define how an element should behave given a new state?

I'm assuming it's in the
render()
method or the
didMount()
method.

In this situation, I need to call a JS method on the DOM element:

if (this.state.play) {
document.querySelector(".VideoPlayer-Video").play();
} else {
document.querySelect(".VideoPlayer-Video").pause();
}


I can't do this before the component has rendered. So how & where should this be done?

Example Details:

This is a very simple react component. It has a
<video>
element, and a button that pauses or plays it.

The state only has one attribute, "play", which is "true" if the video is playing or false if the video is paused.

When the "play" button is clicked, I flip the state, then do a DOM query to get the video element, and call a method on it.

This seems unconventional in React, since I am telling the component how to respond to a state change inside of a click handler. I would expect to define how the component responds to state change elsewhere, but I'm not sure where.

Where is the conventional place to tell the video element to play or not in response to state change? How can I reference the DOM element there?

Code:

var React = require('react');

module.exports = React.createClass({
getInitialState: function(){
return {
play: true
}
},
render: function(){
return <div className="VideoPlayer">
<video className="VideoPlayer-Video" src="/videos/my-video.mov"></video>
<button className="VideoPlayer-PlayButton" onClick={this.handlePlayButtonClick}>
{this.state.play ? "Pause" : "Play"}
</button>
</div>
},
handlePlayButtonClick: function(){
this.setState({
play: !this.state.play
}), function(){
var video = document.querySelector(".VideoPlayer-Video");

if (this.state.play) {
video.play();
} else {
video.pause();
}
}
}
});

Answer

Using componentDidUpdate seems appropriate.

1.Click 2. Change state -> trigger - rerender 3. Just after your component is mounted call the right function for video

componentDidUpdate: function() {
   if (this.state.play) {
        this.refs.MyVideo.play();
   } else {
        this.refs.MyVideo.pause();
   }
}

If you need to control those function for the very first rendering use componentDidMount. Simple and clean in my opinion.

Edit: I edited my code using ref, I think this is indeed the right way to go https://facebook.github.io/react/docs/more-about-refs.html

Just place a ref in your video component:

 <video ref="MyVideo"> </video>

Updated after mark's comment

Comments