this
this

Reputation: 351

React and SetInterval (FCC Pomodoro Timer..)

I'm having some issues using setInterval with React.

Basically, I'm trying to build a Pomodoro timer for FreeCodeCamp, and setInterval is not recognizing that the function is defined.. link below.

https://codepen.io/paalpwmd/pen/yLNJEMv?editors=1111

Basically - I want the timer method to subtract 1000ms from the total length state until the the length is 0.

The error I get is this..

TypeError: this.tick is not a function 
 at https://cdpn.io/boomboom/v2/index.html?editors=1111&key=iFrameKey-713a6d83-4480-ba30-a898-c7e72b527740:28

I've also tried putting the set interval function directly inside the timer (like this), and I get an error saying that this.state is undefined.

timer(){
    setInterval(function() {
      this.setState({
      length: this.state.length - 1000 
}), 1000)
}

I've tried both ES6 and ES5 syntax, and this is bound at the constructor.

I assume that this has something to do with the way that React binds this. What am I doing wrong here?

Upvotes: 1

Views: 133

Answers (1)

Sohail Ashraf
Sohail Ashraf

Reputation: 10569

Change the setInterval callback function to arrow function.

when the setInterval will call the callback function the this will refer to the setInterval object.

Always use the callback in the setState if the state update depends on the previous state.

Try this.

  timer() {
    setInterval(() => {
      this.tick();
      console.log("tick");
    }, 1000)
  }

Working Example:

class PomTimer extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      break: 300000,
      length: 1500000,
      clockFace: ''
    }
    this.clockify = this.clockify.bind(this);
    this.timer = this.timer.bind(this);
    this.reset = this.reset.bind(this);
    this.tick = this.tick.bind(this);
  }

  reset() {
    this.setState({
      break: 300000,
      length: 1500000
    })
    this.clockify();
  }

  tick() {
    //console.log("tick: " + this.state.length);
    if (this.state.length > 0) {
      this.setState( (preState) => {
        return {
            length: preState.length - 1000
        }
      })

    } else {

    }
  }

  clockify() {
    let minutes = Math.floor(this.state.length / 60000);
    let seconds = (this.state.length % 60000) / 1000
    this.setState({
      clockFace: `${minutes}:${seconds}`
    })
  }


  timer() {
    setInterval(() => {
      this.tick();
    }, 1000)
  }

  componentDidMount() {
    this.clockify();
  }
  render() {
    return <div>
      <h1>Pomodoro Clock</h1>
      <div id="break-label">
        <div id="break-increment" onClick={this.breakInc}>+</div>
        <p id="break-length">Break Length: {this.state.break / 60000}</p>
        <div id="break-decrement" onClick={this.breakDec}>-</div>
      </div>

      <div id="session-label">
        <div id="session-increment" onClick={this.sessionInc}> + </div>
        <p id="session-length">Session Length {Math.ceil(this.state.length / 60000)} ({this.state.length})</p>
        <div id="session-decrement" onClick={this.sessionDec}> - </div>

      </div>
      <div id="session">

        <p id="time-left"> Session: {this.state.clockFace} </p>
      </div>
      <button onClick={this.clockify}>test</button>
      <button onClick={this.resetTimer}>Reset</button>
      <button onClick={this.timer}>Timer</button>
    </div>
  }
}
ReactDOM.render(<PomTimer />, document.getElementById("react-root"));
#react-root {
  width: 50%;
  display: flex;
  margin: auto;
  border: 2px solid black;
  border-radius: 50px;
  justify-content: center;
  
}
body {
  background-color: #1E555C;
  font-family: 'Verdana'
}

#break-increment, #break-decrement, #session-increment, #session-decrement{
  cursor: pointer;
  margin: 0px 5px;
  border: 1px solid black;
  border-radius: 5px;
  width: 20px;
  height: 20px;
  text-align: center;
  background-color: #1E777C;
}

#session-label, #break-label, #session {
   display: flex;
  flex-direction: row;
  justify-content: center;
  padding-top: 50px;
}
#break-length, #session-length{
margin: 0;
}

#time-left {
  font-size: 2rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react-root">

Upvotes: 1

Related Questions