25-5-clock STUCK ON LAST BUG

Tell us what’s happening:

Hello all, this is my first time posting. I’m having an issue when running the freeCodeCamp tests for the timer that I just can’t seem to figure out, hopefully someone can enlighten me! Every single test passes except for one…

#14. “When a break countdown reaches zero (NOTE: timer MUST reach 00:00), and a new countdown begins, the element with the id of “timer-label” should display a string indicating a session has begun.”

The part that really confuses me is that there is a nearly identical test…

#12. “When a session countdown reaches zero (NOTE: timer MUST reach 00:00), and a new countdown begins, the element with the id of “timer-label” should display a string indicating a break has begun.”

and this test passes! The error returned says that the timer isn’t reaching 00:00 but I don’t think that is the issue considering you can see that happen. I have moved things around to make them more readable within the forum setting but other than that no errors are being thrown at this time. I am at a loss… any thoughts?

Your code so far

import '../styles/CountdownClock.css';
import React from 'react';
import convertToMinutes from '../functions/convertToMinutes';

class CountdownClock extends React.Component{
  constructor(props){
    super(props);
      this.state = {
        timeRemaining: 1500,      //in seconds
        breakLength: 300,              //in seconds
        sessionLength: 25,             //in minutes
        paused: true,
        onBreak: false
      }
      this.displayTime = this.displayTime.bind(this);
      this.playPause = this.playPause.bind(this);
      this.handlePlayPause = this.handlePlayPause.bind(this);
      this.handleBreak = this.handleBreak.bind(this);
      this.reset = this.reset.bind(this);
      this.increaseSessionLength = this.increaseSessionLength.bind(this);
      this.decreaseSessionLength = this.decreaseSessionLength.bind(this);
      this.increaseBreakLength = this.increaseBreakLength.bind(this);
      this.decreaseBreakLength = this.decreaseBreakLength.bind(this);
  }

/*DISPLAYS TIME IN CLOCK FASHION*/
  displayTime = (minutesAndSeconds) => {       
     let minutes = minutesAndSeconds[0];
     let seconds = minutesAndSeconds[1];
     return (
        <span id='clock-display'>
          {minutes < 0  ?  <h1 className='timer'>00</h1> 
            : minutes < 10 ? <h1 className='timer'>0{minutes}</h1> 
            : <h1 className='timer'>{minutes}</h1>}
          {seconds === null ? <h1 className='timer'> :00</h1>
            : seconds < 10 ? <h1 className='timer'>:0{seconds}</h1> 
            : <h1 className='timer'>:{seconds}</h1>}
         </span>
    )
  }
/*END OF CLOCK-DISPLAY LOGIC*/

/*TOGGLE PAUSE*/
  playPause(){                                                           
    this.setState({paused: !this.state.paused}, this.handlePlayPause);  
  }
/*END OF TOGGLING PAUSE IN STATE*/

/*HANDLES TIMER LOGIC BASED ON CURRENT STATE*/
  handlePlayPause(){                                                 
    if(!this.state.paused){                                              //If the timer ISN'T paused...
      let rightNow = new Date().getTime();         //Make a var equal to the current time in ms
      let OneSecondFromNow = new Date().getTime() + 1000;      
      let interval = setInterval(() => {                                    
        rightNow = new Date().getTime();  
        if(this.state.timeRemaining !== 0 && rightNow > OneSecondFromNow)  
        {                                                   
          this.setState({timeRemaining: this.state.timeRemaining - 1})         
          OneSecondFromNow += 1000;                                               
        }
        else if(this.state.timeRemaining === 0){                                 //If the timer has reached 0...
          clearInterval(localStorage.getItem('interval'));                         //Stop the current loop
          document.getElementById('beep').play();                                    //Play the alarm sound
          this.handleBreak();                                                                                     //Then handleBreak
        }
      }, 30);
      localStorage.clear();                                                 
      localStorage.setItem('interval', interval);                  
    }                                                                            
    else if(this.state.paused){                                                                   //If the state of paused is true...
      clearInterval(localStorage.getItem("interval"));               //End the current loop 
    }
  }
  /*END OF HANDLING TIMER LOGIC*/

  /*HANDLE BREAK WHEN TIMER REACHES ZERO*/
  handleBreak(){
    const newSession = this.state.sessionLength*60;
    setTimeout(() => 
    {
      if(this.state.onBreak){
        this.setState({timeRemaining: newSession, onBreak: false}, () => 
          setTimeout(this.handlePlayPause, 1000));
      }
      if(!this.state.onBreak){
        this.setState({timeRemaining: this.state.breakLength, onBreak: true}, () => 
          setTimeout(this.handlePlayPause, 1000));
      }
    }, 1000)
  }
  /*END OF HANDLING BREAK*/ 

  /*INCREASE BREAK LENGTH*/
  increaseBreakLength(){
    if(this.state.onBreak){
      this.state.breakLength <= 3540 ? 
        this.setState(state => 
           ({
            breakLength: state.breakLength + 60, timeRemaining: state.timeRemaining + 60
           }))
        : null;
    }
    else{
      this.state.breakLength <= 3540 ? 
        this.setState(state => ({breakLength: state.breakLength + 60})) 
        : null;
    }
  }
  /*END OF INCREASE BREAK LENGTH*/

/*DECREASE BREAK LENGTH*/
  decreaseBreakLength(){
    if(this.state.onBreak){
      this.state.breakLength > 60 ? 
        this.setState(state =>
        ({
           breakLength: state.breakLength - 60, timeRemaining: state.timeRemaining - 60
        })) 
        : null;
    }
    else{
      this.state.breakLength > 60 ? 
        this.setState(state => ({breakLength: state.breakLength - 60}))
        : null;
    }
  }
/*END OF DECREASE BREAK LENGTH*/

/*INCREASE SESSION LENGTH*/
  increaseSessionLength(){
    if(!this.state.onBreak){
      this.state.sessionLength <= 59 ? 
        this.setState(state => ({
          sessionLength: state.sessionLength + 1 , timeRemaining: state.timeRemaining + 60
        })) 
        : null;
    }
    else{
      this.state.sessionLength <= 59 ? 
        this.setState(state => ({sessionLength: state.sessionLength + 1}))
        : null;
    }
  }
/*END OF INCREASE SESSION LENGTH*/

/*DECREASE SESSION LENGTH*/
  decreaseSessionLength(){
    if(!this.state.onBreak){
      this.state.timeRemaining > 60 ? 
        this.setState(state => 
        ({
           sessionLength: state.sessionLength - 1, timeRemaining: state.timeRemaining - 60
        })) 
        : null;
    }
    else{
      this.state.sessionLength > 1 ?
        this.setState(state => ({sessionLength: state.sessionLength - 1})) 
        : null;
    }
  }
  /*END OF DECREASE SESSION LENGTH*/

  /*RESET STATE*/
  reset(){                                                              
    document.getElementById('beep').pause();
    document.getElementById('beep').currentTime = 0;
    this.setState({
      timeRemaining: 1500, breakLength: 300, sessionLength: 25, paused: true, onBreak: 
      false}, this.playPause())
  }
  /*END OF STATE RESET*/

  render(){
    return(
      <div id='app'>
        <audio id='beep' src='alarm.mp3'></audio>
        <h1 className='title'>25 + 5 CLOCK</h1>
        <div className='sessionBreakRowHeaders'>
          <span id='break-label'>Break Length</span> <span id='session-label'>Session Length</span>
        </div>
        <div className='sessionBreakRow'>
          <div className='buttonRow'>
            <button id='break-increment' onClick={() => this.increaseBreakLength()}>UP</button>
            <h1 id='break-length'>{convertToMinutes(this.state.breakLength)}</h1>
            <button id='break-decrement' onClick={() => this.decreaseBreakLength()}>DOWN</button>
          </div>
          <div className='buttonRow'>
            <button id='session-increment' onClick={() => this.increaseSessionLength()}>UP</button>
            <h1 id='session-length'>{this.state.sessionLength}</h1>
            <button id='session-decrement' onClick={() => this.decreaseSessionLength()}>DOWN</button>
          </div>
        </div>
        <div className='timerDisplay'>
          <h1 id='timer-label'>{this.state.onBreak ? 'Break' : 'Session'}</h1>
          <h1 id='time-left'>{this.displayTime(convertToMinutes(this.state.timeRemaining))}</h1>
        </div>
        <div className='buttonRow'>
          <button id='start_stop' className='playPause' onClick={this.playPause}>
            {this.state.paused && 'play'}{!this.state.paused && 'pause'}
          </button>
          <button id='reset' onClick={this.reset}>Reset</button>
        </div>
      </div>
    )
  }
}

export default CountdownClock

Errors:

Thanks again!
Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.35

Challenge: Front End Development Libraries Projects - Build a 25 + 5 Clock

Link to the challenge:
https://www.freecodecamp.org/learn/front-end-development-libraries/front-end-development-libraries-projects/build-a-25--5-clo

Just post a link to the live project on codepen so we can test it. Images of code are not as helpful as code, which is not as helpful as the project.

Without access to the project, this sounds like this.

1 Like

Sorry about that, I reached the maximum number of replies and I had never used codepen so it took a little while to figure out, but here it is. https://codepen.io/Christianh467/pen/abKJodb
I tried setting ({state => }) to ({newState =>}) but to no avail.

P.S. I couldn’t source the audio on a free codepen account so disregard those errors

One problem I see is if I keep clicking the Down Session Length button, the number between the buttons stops at 1, but the Session time will go down to 00:10.

Peek 2022-11-09 22-08

Also, if you watch the tests running, you will see the Session Length go to -36 for test #24 at some point.

Another issue is that you should not have both of these lines:

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

Get rid of the bottom one since you are using ReactDom.createRoot() and then root.render().

I had left that in to make testing faster but I have reverted it back so that it won’t do that any more. However, if you prefer it the other way I’ve left the code in but commented it out.

I can’t figure out where the -36 is coming from on test #24…the code should prevent any decrease of session or break length if either are less than 1…at a loss there.

Thank you I did go ahead and remove that last line, I wasn’t sure if it was needed or not.

The -36 is this (@RandellDawson).

This hits projects that set state with a function instead of a value, so when test #24 decrements 60 times (twice, inadvertently) the first set is getting batched while checking against current state (so you get 60 decrements while the checks think it’s still ~25; that’s my theory anyway), hence the -36.

Also, linking to audio files on codepen works. You don’t have to store the files there.

2 Likes

(@jeremy.a.gray) You were absolutely right. I reverted back to React 17 and everything ran fine. Thank you, I would have never have guessed React was the issue…been pulling my hair out over this forever. Certified now though, thanks to you!