25 + 5 clock / Using setInterval to update component state

Tell us what’s happening:
Hello fellow coders :wave:,

I’m trying to tackle user story 18 of the 25 + 5 clock project, which consist in running the counter after pressing start. That’s the :orange_heart: of the project :slightly_smiling_face:

I added an onClick event to the start button with the following eventHandler:

 let timer;
  const handleStartAndStop = () => {
    const handleCountdown = () => {
      setTimeRemaining(timeRemaining - 1);
    }
    timer = setInterval(handleCountdown, 1000);
  }

But when running my code, it decreased timeRemaining state by 1 and that’s it. I tried console logging stuff within the setInterval callback and it works fine, so I guess my problem is really with the update of the setTimeRemaining within the setInterval callback.

I suspect an issue with async or the event loop, but I’m too noob to figure at the moment. Any help would be greatly appreciated :pray:

Your code so far

// IMPORTING STYLE
import "./App.scss";

//IMPORTING REACT STUFF
import { useState, useEffect } from "react";


function App() {
  const [breakDuration, setBreakDuration] = useState(5);
  const [sessionDuration, setSessionDuration] = useState(25);
  const [timeRemaining, setTimeRemaining] = useState(sessionDuration);
  const [timerRunning, setTimerRunning] = useState(false);
  const handleReset = () => {
    setTimerRunning(false);
    setBreakDuration(5);
    setSessionDuration(25);
    setTimeRemaining(sessionDuration*60);
  };
  let timer;
  const handleStartAndStop = () => {
    const handleCountdown = () => {
      setTimeRemaining(timeRemaining - 1);
    }
    timer = setInterval(handleCountdown, 1000);
  }
  useEffect(() => {
    setTimeRemaining(sessionDuration*60);
  }, [sessionDuration])
  return (
    <div className="App">
      <div className="controls">
        <div id="break-label">
          <p>BREAK LENGTH</p>
          <button onClick={() => breakDuration > 0? setBreakDuration(breakDuration - 1) : 0} id="break-decrement">-</button>
          <label id="break-length">{breakDuration}</label>
          <button onClick={() => breakDuration < 60? setBreakDuration(breakDuration + 1) : 60} id="break-increment">+</button>
        </div>
        <div id="session-label">
          <p>SESSION LENGTH</p>
          <button onClick={() => sessionDuration > 0? setSessionDuration(sessionDuration - 1) : 0} id="session-decrement">-</button>
          <label id="session-length">{sessionDuration}</label>
          <button onClick={() => sessionDuration > 0? setSessionDuration(sessionDuration + 1) : 0} id="session-increment">+</button>
        </div>
      </div>
      <div id="timer-label">
        <p>SESSION</p>
        <p id="time-left">{timeRemaining}</p>
        <button onClick={handleStartAndStop} id="start_stop">Start</button>
        <button onClick={handleReset} id="reset">Reset</button>
      </div>
    </div>
  );
}

export default App;

Your browser information:

User Agent is: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36

Challenge: Build a 25 + 5 Clock

Link to the challenge:

When you call handleStartAndStop, the interval is trying to set the new version of state based on the value that it was at the time that the function was called. You can use a callback inside of setTimeRemaining, so that it always updates based on the latest version of state.

const handleCountdown = () => {
    setTimeRemaining(prevState => prevState - 1);
}

Thanks Jonathan, it worked just fine. I kinda get what you meant and it actually helped to grasp a bit more the concept of async, and how callback used to be the solution to handle it.

Many thanks for helping me on the journey! If you have any good reading to help push through this concept of async, would be dope as I still haven’t found the one explnation that makes it click!