Ilya Krasnov
Ilya Krasnov

Reputation: 231

componentDidMount seems not to be called after setState in ReactJS

Learning ReactJS I am working on a simple countdown app. The Countdown component looks like this:

const Countdown = React.createClass({
  getInitialState: function() {
    return {
      remaining: 10,
      running: true,
    };
  },
  componentDidMount: function(){
    if (this.state.running) {
      setInterval(this.tick, 1000);
    } else {
      console.log("already paused");
    };
  },
  tick: function(){
    if (this.state.remaining === 0) {
      this.setState({ remaining: 10 });
      this.props.updateStats();
    } else {
        this.setState({ remaining: this.state.remaining -1 });
    };

  },
  handlePauseClick: function(){
    this.setState({ running: false });
  },
  render: function(){
    const s = this.state.remaining;
    return (
      <div className='ui centered card'>
        <div className='content centered'>
          <h1>{helpers.secondsToHuman(s)}</h1>
          <ButtonControls
            handlePauseClick={this.handlePauseClick}
          />
        </div>
      </div>
    )
  },
});

The countdown starts running. When clicking on the pause button it is supposed to stop. The componentDidMount only runs the timer when running is true:

if (this.state.running) {
  setInterval(this.tick, 1000);
} else {
  console.log("already paused");
};

After handling the click with:

this.setState({ running: false });

I was expecting that the component gets re-rendered and that componentDidMount will be executed again. This is not happening though. componentDidMount seems only to run once.

Thanks for your ideas

Upvotes: 7

Views: 3953

Answers (2)

Lyubomir
Lyubomir

Reputation: 20027

componentDidMount gets executed only once, when the React component is mounted to the tree, that's why it is not called after setState.

So you need componentDidUpdate, this callback gets executed on every rerender except for the initial one. For the initial one you may want to use componentDidMount.

Also, you are not clearing the interval in the else block, which means that the tick function will not stop executing even if the component is rerendered again.


Try this

componentDidMount: function() {
   this.tickInterval = setInterval(this.tick, 1000);
},

componentDidUpdate: function() {
  // if app is running and there is no tickInterval yet, set it
  if (this.state.running && this.tickInterval === null) {
    this.tickInterval = setInterval(this.tick, 1000);
  // if app is stopped, but there is a tickInterval, clear it
  } else if (!this.state.running && this.tickInterval !== null) {
    clearInterval(this.tickInterval);
    this.tickInterval = null;
  }
},

componentWillUnmount() { 
  clearInterval(this.tickInterval);
}

Upvotes: 14

user5520186
user5520186

Reputation:

componentDidMount is only executed when the initial render process is finished. You should use componentWillUpdate or componentDidUpdate for your task.

Upvotes: 1

Related Questions