0

In this React Pomodoro clock, setInterval calls down every time state.timeLeftSeconds is reduced. However, when attempting to pass the freecodecamp tests 12 and 13, there is a message: Break time didn't start with the correct value.: expected 60 to be at most 5. Yet, when the clock is run the code appears to be working perfectly. Any help would be greatly appreciated. The testing suite is also there in the code sandbox: https://codesandbox.io/s/xenodochial-fog-981wl

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './style.css';

/*
* A simple React component
*/
const initState = {
  breakLength: 5,
  sessionLength: 25,
  init: 'session',
  stateIndex: 0,
  timeLeft: undefined,
  timeLeftSeconds: undefined,
  started: false,
  intervalFunc: undefined
}

const states = [ { name: 'session', duration: 1500 }, { name: 'break', duration: 300 } ]

const secondsToMins = (time) => {
  let converted = ('0' + Math.floor(time / 60)).slice(-2) + ':' + ('0' + Math.floor(time % 60)).slice(-2);
  return converted;
}

class Clock extends React.Component {

  constructor(props) {
    super(props);
    this.state = initState;
    this.breakDecrement = this.breakDecrement.bind(this);
    this.breakIncrement = this.breakIncrement.bind(this);
    this.sessionDecrement = this.sessionDecrement.bind(this);
    this.sessionIncrement = this.sessionIncrement.bind(this);
    this.startStop = this.startStop.bind(this);
    this.reset = this.reset.bind(this);
  }

  componentDidMount() {
    let sessionSeconds = this.state.sessionLength * 60;
    this.setState({ timeLeftSeconds: sessionSeconds });
    this.setState({ timeLeft: secondsToMins(sessionSeconds) });
  }

  breakDecrement() {
    // decrements the breakLength and the breakSeconds
    // breakLength is only a number ie. 5 (does not show seconds)
    // breakSeconds is that nunber converted into seconds
    let breakLength = this.state.breakLength - 1;
    if (breakLength > 0 && breakLength < 61){
      this.setState({ breakLength: breakLength });
      let breakSeconds = breakLength * 60;
      states[1]['duration'] = breakSeconds;
    }
  }

  breakIncrement() {
    // same as decrement except does increment
    let breakLength = this.state.breakLength + 1;
    if (breakLength > 0 && breakLength < 61){
      this.setState({ breakLength: breakLength });
      let breakSeconds = breakLength * 60;
      states[1]['duration'] = breakSeconds;
    }
  }

  sessionDecrement() {
    // decrements the sessionLength and the sessionSeconds
    // sessionLength is only a number ie. 25 (does not show seconds)
    // sessionSeconds is that nunber converted into seconds
    let sessionLength = this.state.sessionLength - 1;
    if (sessionLength > 0 && sessionLength < 61){
      states[0]['duration'] = sessionLength*60;
      this.setState(prevState => ({
        sessionLength: prevState.sessionLength-1,
        timeLeftSeconds: (prevState.sessionLength-1)*60,
        timeLeft:  secondsToMins((prevState.sessionLength-1)*60)})
      );
    }
  }

  sessionIncrement() {
    // same as decrement except does increment
    let sessionLength = this.state.sessionLength + 1;
    if (sessionLength > 0 && sessionLength < 61){
      states[0]['duration'] = sessionLength*60;
      this.setState(prevState => ({
        sessionLength: prevState.sessionLength+1,
        timeLeftSeconds: (prevState.sessionLength+1)*60,
        timeLeft:  secondsToMins((prevState.sessionLength+1)*60)})
      );
    }
  }

  startStop(id) {
    // starts the countDown, which runs continuously until the start/stop button
    // is pressed again, which pauses the countdown.
    // the id parameter is used by countDown to play the audio beep
    if(!this.state.started){
      this.countDown(id);
      this.setState({ started: true});
    }
    // pauses the countDown
    if(this.state.started){
      let intervalFunc = this.state.intervalFunc;
      clearInterval(intervalFunc);
      this.setState({ started: false});
    }
  }

  reset() {
    let intervalFunc = this.state.intervalFunc;
    clearInterval(intervalFunc);
    // reset state to default values
    this.setState({ breakLength: 5 });
    this.setState({ sessionLength: 25 });
    this.setState({ init: 'session' });
    this.setState({ timeLeftSeconds: 1500})
    this.setState({ timeLeft: '25:00' });
    this.setState({ stateIndex: 0 });
    this.setState({ started: false });
    this.setState({ intervalFunc: undefined });
  }


  decreaseCurrentSecond = () => this.state.timeLeftSeconds--;

  countDown(id) {
    // set the function to a variable and set state to it, so the function
    // can be paused with clearInterval()
    var intervalFunc = setInterval(
      () => down(this.decreaseCurrentSecond()),
      1000
    );
    this.setState({ intervalFunc: intervalFunc });


    const down = (time) => {
      if (time > 0) {
        // converts seconds to MM:SS at every t-minus
        this.setState({ timeLeft: secondsToMins(time) });
        /*
        console.log(time);
        console.log(this.state.timeLeft);*/
      }

      let sound = document.getElementById(id).childNodes[0];

      if (time <= 0 ) {
        sound.play();
        this.setState({ timeLeft: secondsToMins(time) });

        console.log(time);
        console.log(this.stateIndex);
        console.log(this.state.init);
        console.log(this.state.timeLeftSeconds);

        let stateIndex = (this.state.stateIndex + 1) % states.length;
        this.setState({ stateIndex: stateIndex });
        this.setState({ init: states[stateIndex]['name'] });
        this.setState({ timeLeftSeconds: states[stateIndex]['duration'] });

        console.log(time);
        console.log(this.stateIndex);
        console.log(this.state.init);
        console.log(this.state.timeLeftSeconds);
      }
    };

    down(this.decreaseCurrentSecond());
  }

  render() {
    return (
      <div id="clock">
      <h1 id="title">25-5 Clock</h1>

      <div>
      <p id="break-label">Break Length</p>
      <p id="break-length">{this.state.breakLength}</p>
      <button id="break-decrement" onClick={e => this.breakDecrement()}> Decrease </button>
      <button id="break-increment" onClick={e => this.breakIncrement()}> Increase </button>
      </div>

      <div>
      <p id="session-label">Session Length</p>
      <p id="session-length">{this.state.sessionLength}</p>
      <button id="session-decrement" onClick={e => this.sessionDecrement()}> Decrease </button>
      <button id="session-increment" onClick={e => this.sessionIncrement()}> Increase </button>
      </div>

      <hr/>

      <div>
      <p id="timer-label">{this.state.init}</p>
      <p id="time-left">{this.state.timeLeft}</p>
      <button id="start_stop" onClick={e => this.startStop(e.target.id)}><audio id="beep" src='./beep.mp3'></audio> start/stop </button>
      <button id="reset" onClick={e => this.reset()}> reset </button>
      </div>

      </div>
    );
  }
};

/*
* Render the above component into the div#app
*/
ReactDOM.render(<Clock />, document.getElementById("app"));

index.html

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <title>25-5 Clock</title>
  <style>
  </style>
</head>
<body>
  <main>
    <div id="app"></app>
    </main>
  </body>
  </html>
65535
  • 523
  • 1
  • 10
  • 32

1 Answers1

1

The reason those tests are failing is because the timeLeftSeconds in the state isn't being set properly once the clock goes into the 'break' cycle and vice versa. I have made the necessary adjustments below. Notice the changes in the decreaseCurrentSecond and down methods.

Also, never try to update values in the state directly like you did here - decreaseCurrentSecond = () => this.state.timeLeftSeconds--;. This is not good practice - Why can't I directly modify a component's state, really?

import React from "react";
import ReactDOM from "react-dom";
import "./style.css";

/*
 * A simple React component
 */
const initState = {
  breakLength: 0.5,
  sessionLength: 0.5,
  init: "session",
  // stateIndex: 0,
  timeLeft: undefined,
  timeLeftSeconds: undefined,
  started: false,
  intervalFunc: undefined
};

const secondsToMins = (time) => {
  let converted =
    ("0" + Math.floor(time / 60)).slice(-2) +
    ":" +
    ("0" + Math.floor(time % 60)).slice(-2);
  return converted;
};

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = initState;
    this.breakDecrement = this.breakDecrement.bind(this);
    this.breakIncrement = this.breakIncrement.bind(this);
    this.sessionDecrement = this.sessionDecrement.bind(this);
    this.sessionIncrement = this.sessionIncrement.bind(this);
    this.startStop = this.startStop.bind(this);
    this.reset = this.reset.bind(this);
  }

  componentDidMount() {
    let sessionSeconds = this.state.sessionLength * 60;
    this.setState({ timeLeftSeconds: sessionSeconds });
    this.setState({ timeLeft: secondsToMins(sessionSeconds) });
  }

  breakDecrement() {
    console.log("break decrement");
    // decrements the breakLength and the breakSeconds
    // breakLength is only a number ie. 5 (does not show seconds)
    // breakSeconds is that nunber converted into seconds
    let breakLength = this.state.breakLength - 1;
    if (breakLength > 0 && breakLength < 61) {
      this.setState({ breakLength: breakLength });
      // let breakSeconds = breakLength * 60;
      // states[1]["duration"] = breakSeconds;
    }
  }

  breakIncrement() {
    // same as decrement except does increment
    let breakLength = this.state.breakLength + 1;
    if (breakLength > 0 && breakLength < 61) {
      this.setState({ breakLength: breakLength });
      // let breakSeconds = breakLength * 60;
      // states[1]["duration"] = breakSeconds;
    }
  }

  sessionDecrement() {
    // decrements the sessionLength and the sessionSeconds
    // sessionLength is only a number ie. 25 (does not show seconds)
    // sessionSeconds is that nunber converted into seconds
    let sessionLength = this.state.sessionLength - 1;
    if (sessionLength > 0 && sessionLength < 61) {
      // states[0]["duration"] = sessionLength * 60;
      this.setState((prevState) => ({
        sessionLength: prevState.sessionLength - 1,
        timeLeftSeconds: (prevState.sessionLength - 1) * 60,
        timeLeft: secondsToMins((prevState.sessionLength - 1) * 60)
      }));
    }
  }

  sessionIncrement() {
    // same as decrement except does increment
    let sessionLength = this.state.sessionLength + 1;
    if (sessionLength > 0 && sessionLength < 61) {
      // states[0]["duration"] = sessionLength * 60;
      this.setState((prevState) => ({
        sessionLength: prevState.sessionLength + 1,
        timeLeftSeconds: (prevState.sessionLength + 1) * 60,
        timeLeft: secondsToMins((prevState.sessionLength + 1) * 60)
      }));
    }
  }

  startStop(id) {
    // starts the countDown, which runs continuously until the start/stop button
    // is pressed again, which pauses the countdown.
    // the id parameter is used by countDown to play the audio beep
    if (!this.state.started) {
      this.setState({ started: true });
      this.countDown(id);
    }
    // pauses the countDown
    if (this.state.started) {
      this.setState({ started: false });
      let intervalFunc = this.state.intervalFunc;
      clearInterval(intervalFunc);
    }
  }

  reset() {
    let intervalFunc = this.state.intervalFunc;
    clearInterval(intervalFunc);
    // reset state to default values
    this.setState({ breakLength: 5 });
    this.setState({ sessionLength: 25 });
    this.setState({ init: "session" });
    this.setState({ timeLeftSeconds: 1500 });
    this.setState({ timeLeft: "25:00" });
    // this.setState({ stateIndex: 0 });
    this.setState({ started: false });
    this.setState({ intervalFunc: undefined });
  }

  decreaseCurrentSecond = () =>
    //this.state.timeLeftSeconds--;
    {
      this.setState({
        timeLeftSeconds: this.state.timeLeftSeconds - 1
      });
      return this.state.timeLeftSeconds;
    };

  countDown(id) {
    // set the function to a variable and set state to it, so the function
    // can be paused with clearInterval()
    var intervalFunc = setInterval(
      () => down(this.decreaseCurrentSecond()),
      1000
    );
    this.setState({ intervalFunc: intervalFunc });

    const down = (time) => {
      if (time > 0) {
        // converts seconds to MM:SS at every t-minus
        this.setState({ timeLeft: secondsToMins(time) });
        /*
        console.log(time);
        console.log(this.state.timeLeft);*/
      }

      let sound = document.getElementById(id).childNodes[0];

      if (time <= 0) {
        sound.play();
        this.setState({ timeLeft: secondsToMins(time) });

        console.log(`##########`);
        console.log(time);
        // console.log(this.stateIndex);
        console.log(this.state.init);
        console.log(this.state.timeLeftSeconds);
        console.log(`##########`);

        // let stateIndex = (this.state.stateIndex + 1) % states.length;
        // this.setState({ stateIndex: stateIndex });
        this.setState({
          init: this.state.init === "session" ? "break" : "session"
        });
        this.setState({
          timeLeftSeconds:
            this.state.init === "session"
              ? this.state.sessionLength * 60 + 1
              : this.state.breakLength * 60 + 1
        });

        console.log(`##########`);
        console.log(time);
        // console.log(this.stateIndex);
        console.log(this.state.init);
        console.log(this.state.timeLeftSeconds);
        console.log(`##########`);
      }
    };

    // down(this.decreaseCurrentSecond());
  }

  render() {
    return (
      <div id="clock">
        <h1 id="title">25-5 Clock</h1>

        <div>
          <p id="break-label">Break Length</p>
          <p id="break-length">{this.state.breakLength}</p>
          <button id="break-decrement" onClick={(e) => this.breakDecrement()}>
            {" "}
            Decrease{" "}
          </button>
          <button id="break-increment" onClick={(e) => this.breakIncrement()}>
            {" "}
            Increase{" "}
          </button>
        </div>

        <div>
          <p id="session-label">Session Length</p>
          <p id="session-length">{this.state.sessionLength}</p>
          <button
            id="session-decrement"
            onClick={(e) => this.sessionDecrement()}
          >
            {" "}
            Decrease{" "}
          </button>
          <button
            id="session-increment"
            onClick={(e) => this.sessionIncrement()}
          >
            {" "}
            Increase{" "}
          </button>
        </div>

        <hr />

        <div>
          <p id="timer-label">{this.state.init}</p>
          <p id="time-left">{this.state.timeLeft}</p>
          <button id="start_stop" onClick={(e) => this.startStop(e.target.id)}>
            <audio id="beep" src="./beep.mp3"></audio> start/stop{" "}
          </button>
          <button id="reset" onClick={(e) => this.reset()}>
            {" "}
            reset{" "}
          </button>
        </div>
      </div>
    );
  }
}

/*
 * Render the above component into the div#app
 */
ReactDOM.render(<Clock />, document.getElementById("app"));

https://codesandbox.io/s/heuristic-star-i3t8m?file=/src/index.js

ultimoTG
  • 853
  • 6
  • 9