React: How to prevent a setInterval from being delayed after a button is clicked to start it?

65535

In this React Pomodoro Clock, there is a function countDown which starts setInterval, however, when the button id="start_stop" is clicked to start countDown, there is some delay before starting. Any help would be greatly appreciated. CodePen.io: https://codesandbox.io/s/tender-wozniak-kfuss

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 });
  }

  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.state.timeLeftSeconds--), 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);
      }

      if (time <= 0) {

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

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

        console.log(time);
        console.log(this.state.timeLeft);
        console.log(this.state.init);
      }
    }
  }

  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>
 </script>
  </body>
  </html>
Viet Dinh

You have 2 issues:

  • Set interval will call down after 1s. (delay 1 second)
  • you have method call method like: down(this.state.timeLeftSeconds--) will be first time down with current time, no change time here and then this.state.timeLeftSeconds will decrease later. (delay 1 second)

=> You will delay 2 seconds with your code.

My suggest is "you should call down function right away and modify way down the timeLeftSeconds"

descreaseCurrentSecond = () => --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.descreaseCurrentSecond()),
        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);
        }

        if (time <= 0) {
            let sound = document.getElementById(id).childNodes[0];
            sound.play();

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

            console.log(time);
            console.log(this.state.timeLeft);
            console.log(this.state.init);
        }
    };
    
    down(this.descreaseCurrentSecond());
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How to prevent button from being clicked more than 1 in a lifetime

Prevent button from being clicked programmatically

How to change a variables value from a ComboBox after a button being clicked?

How can I prevent multiple ajax queries from being run on "prev" button being clicked multiple times

Prevent parent category radio button from being clicked

Button color after being clicked

How to prevent a disabled JMenuItem from hiding the menu when being clicked?

Prevent <button> being clicked without disabling it

How to prevent a simple React modal from being closed if you either start or end the click event inside the modal?

How to make a button stop listening after being clicked

Swift - How to disable a button after being clicked 3 times in a row?

how to make a button be inactive after being clicked dart flutter

How to disable html button for 24hrs after being clicked

how to prevent jsp tags from being reused after being classloaded

How to start checking for click after a button is clicked in javascript?

How to prevent HTML button from moving when clicked

A button remains in pressed state after being clicked

Button will not appear or will disappear after being clicked

button showing "undefined" after being clicked

The UI styling of Button is not displayed after being clicked

How can I prevent a child from being clicked when the parent is being dragged?

How to set state with a setInterval() when a button is clicked?

How can I prevent a form button being clicked lots of times very fast

Hide React button after it is clicked

CheckComboBox : How to prevent a combobox from closing after a selection is clicked?

How to set the props after a button is clicked in React.js

React Typescript: How to change color of button after it is clicked?

How to prevent regex characters from being changed after page is rendered?

How to prevent FragmentManager from being destroyed after rotation