简体   繁体   中英

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

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

Flux is good consideration for long term components, yes it's very big and requires some breaking changes, as François Richard noticed. But still worth it.

If you have a big component, then here's some tips: You change state of that video component to playing , ONLY when video component receives $event, otherwise it doesn't make sense, as flux heavily uses nodejs eventEmitter, note video components must be explicitly separated, so when you expand your functionality it won't hurt your component.
Simple demo repo .

在DOM上执行的方法应该在componentDidUpdate调用,因为一旦react完成对DOM的更新就会调用它。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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