Pomodoro Project: this.getTimeRemaining is not a function error

I’m refactoring my pomodoro project to use date objects because using plain numbers in the minutes and seconds slots did not allow my project to pass the tests. I’m really stuck as to why my new function getTimeRemaining is not being recognized. So far I am calling it from componentDidMount().

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      sessionLength: 25,
      breakLength: 5,
      sessionType: 'Session',
      intervalId: '',
      currentCount: 25 * 60,
      time: {},
      isPaused: true,
    }
    this.getTimeRemaining = this.getTimeRemaining.bind(this);
    this.startStop = this.startStop.bind(this);
    this.timer = this.timer.bind(this);
    this.reset = this.reset.bind(this);
    this.changeTime = this.changeTime.bind(this);
  }
  componentDidMount() {
    let currentTime = Date.parse(new Date());
    let deadline = new Date(currentTime + 25*60*1000);
    let timeinterval = setInterval(function(){
      let t = this.getTimeRemaining(deadline);
      this.setState({ time:{m:t.minutes, s:t.seconds} });
      console.log(this.state.time);
      if(t.total<=0){
        clearInterval(timeinterval);
      }
    },1000);
  }
  getTimeRemaining(endtime) {
    let t = Date.parse(endtime) - Date.parse(new Date());
    let seconds = Math.floor( (t/1000) % 60 );
    let minutes = Math.floor( (t/1000/60) % 60 );
    return {
      'minutes': minutes,
      'seconds': seconds
    };
  }
  componentWillUnmount() {
     // use intervalId from the state to clear the interval
     clearInterval(this.state.intervalId);
  }
  startStop(e) {
    let intervalId;
    e.target.classList.toggle("fa-snowflake");
    this.state.isPaused = !this.state.isPaused;
    if (this.state.isPaused) {
      clearInterval(this.state.intervalId);
    } else {
      intervalId = setInterval(this.timer, 1000) 
      this.setState({intervalId: intervalId});
    }
  }
  timer() {
    if(this.state.currentCount >= 0) {
      this.setState({ currentCount: this.state.currentCount -1, time:{m:minutes, s:seconds} });
    } else {
      if (this.state.sessionType == 'Session') {
        this.setState({
          sessionType: 'Break',
          currentCount: this.state.breakLength * 60,
          time: {m:this.state.breakLength,s:'00'},
        });
      } else {
        this.setState({
          sessionType: 'Session',
          currentCount: this.state.sessionLength * 60,
          time: {m:this.state.sessionLength,s:'00'},
        });
      }
    }
  }
  reset() {
    clearInterval(this.state.intervalId);
    this.setState({
      isPaused: true,
      sessionLength: 25,
      breakLength: 5,
      time: {m:25, s:'00'},
      currentCount: 25 * 60,
      sessionType: 'Session'
    });
  }
  changeTime(e) {
    if(e.target.id.includes('increment')) {
      if(e.target.id.includes('session') && this.state.sessionLength < 60) {
        this.setState({
          sessionType: 'Session',
          sessionLength: this.state.sessionLength +1,
          time: {m: this.state.sessionLength +1, s: '00'},
          currentCount: (this.state.sessionLength +1) * 60
        });
      } else if (e.target.id.includes('break') && this.state.breakLength < 60) {
        this.setState({
          sessionType: 'Break',
          breakLength: this.state.breakLength +1,
          time: {m: this.state.breakLength +1, s: '00'},
          currentCount: (this.state.breakLength +1) * 60
        });
      }
    } else {
      if(e.target.id.includes('session') && this.state.sessionLength > 1) {
        this.setState({
          sessionType: 'Session',
          sessionLength: this.state.sessionLength -1,
          time: {m: this.state.sessionLength -1, s: '00'},
          currentCount: (this.state.sessionLength -1) * 60
        });
      } else if (e.target.id.includes('break') && this.state.breakLength > 1) {
        this.setState({
          sessionType: 'Break',
          breakLength: this.state.breakLength -1,
          time: {m: this.state.breakLength -1, s: '00'},
          currentCount: (this.state.breakLength -1) * 60
        });
      }
    }
  }
  render() {
    return (
      <div id="container">
        <header>
          <h1>Pomodoro Clock</h1>
        </header>
        <div id="timer">
          <div id="clock">
            <label id="timer-label">{this.state.sessionType}</label>
            <label id="time-left">{this.state.time.m}:{this.state.time.s}</label>
          </div>
          <div id="controls">
            <i id="start_stop" onClick={this.startStop} class="fas fa-magic"></i>
            <i id="reset" onClick={this.reset} class="fas fa-fire"></i>
          </div>
        </div>
        <div id="settings">
          <div id="session">
            <label id="session-label">Session</label>
            <div id="session-settings">
              <span id="session-length">{this.state.sessionLength}</span>
              <div id="session-buttons">
                <i id="session-increment" onClick={this.changeTime} class="fas fa-sort-up"></i>
                <i id="session-decrement" onClick={this.changeTime} class="fas fa-sort-down"></i>
              </div>
            </div>
          </div>
          <div id="break">
            <label id="break-label">Break</label>
            <div id="break-settings">
              <span id="break-length">{this.state.breakLength}</span>
              <div id="break-buttons">
                <i id="break-increment" onClick={this.changeTime} class="fas fa-sort-up"></i>
                <i id="break-decrement" onClick={this.changeTime} class="fas fa-sort-down"></i>
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }
}

console.clear();

ReactDOM.render(<App />, document.getElementById('root'));

Because the way you are defining the callback function for setInterval is creating a new scope inside of the callback and thus ‘this’ does not refer to the class. There is another way you can define the callback that allows ‘this’ to refer to the class.

Thanks so much :partying_face: