25 + 5 Clock: (React) Timer not displaying "00:00". Any help appreciated!

Hey guys! I’m noticing that my timer is not displaying “00:00”, and instead switching to the next session after “00:01”. I’ve checked all the other forums with this problem and haven’t yet been able to find a solution. When I console log the timer, I’m seeing the “0” value come through. I changed the first useEffect so it would only change the session type when the timer reached 0, but that didn’t seem to help. Any help would be greatly appreciated! I’ve been working on this for over a week now, and am ready to have it done :slight_smile: I’m not worried about the audio element yet, and want to get all my timer tests passing before I work on that. Also I’m sorry about how messy this code is, I plan on cleaning it up before finishing.

Main file :

import React, { useState, useEffect } from "react";
import ControlInterval from "./ControlInterval";
import TimeLeft from "./TimeLeft";
import alarm from "./alarm.wav";



const StudyClock = () => {

  const [sessionTime, setSessionTime] = useState(25 * 60);
  const [breakTime, setBreakTime] = useState(5 * 60);
  const [timer, setTimer] = useState(sessionTime);
  const [intervalId, setIntervalId] = useState(null);
  const [sessionType, setSessionType] = useState("Session");
  const seconds = Math.floor(timer % 60);
  const intervalOn = intervalId !== null

  const playAudio = () => {
    let alert = new Audio(alarm)
    alert.play(alarm)
  }

  useEffect(() => {
      if (timer === 0) {
        if (sessionType === "Session") {
          setSessionType("Break");
          setTimer(breakTime);
        } else if (sessionType === "Break") {
          setSessionType("Session");
          setTimer(sessionTime)

        }
      }
    }
  , [breakTime, sessionTime, timer])

  useEffect(() => {
    setTimer(sessionType ? sessionTime : breakTime)
  }, [sessionTime, breakTime])

  const handleStartStop = () => {
    if (intervalOn) {
      if (intervalId) {
        clearInterval(intervalId)
      }
      setIntervalId(null);
    } else {
      const newIntervalId = setInterval(() => {
        setTimer(prevTimeLeft => prevTimeLeft - 1);
      }, 1000); setIntervalId(newIntervalId)
    }
  }


  const controlReset = () => {
    clearInterval(intervalId)
    setIntervalId(null);
    setSessionTime(25 * 60);
    setBreakTime(5 * 60);
    setTimer(sessionTime);
    setSessionType("Session");
  }

  return (
    <div id="container" className="flex h-screen justify-center items-center ">

      <ControlTime
        setTime={setBreakTime}
        time={breakTime}
        type="break" />

      <TimeLeft
        timer={timer}
        sessionType={sessionType}
        handleStartStop={handleStartStop}
        seconds={seconds}
        intervalOn={intervalOn}
        TimeLeft={TimeLeft}
      />

      <ControlTime
        setTime={setSessionTime}
        time={sessionTime}
        type="session" />

      <button onClick={controlReset} className="border-2 border-stone-400 rounded m-2 p-2 font-mono hover:border-red-700" id="reset">reset</button>
    </div>
  );
}

export default StudyClock;

My Timer Component:

import React from "react";

const TimeLeft = ({ seconds, timer, intervalOn, sessionType, handleStartStop }) => {
    return (
        < div id="timer-container" className="border-2 border-black uppercase rounded">
            <p id="timer-label" className="text-center font-mono text-sm m-2">{sessionType}</p>
            <p className="text-center m-2 font-mono">Time Remaining:</p>
            <div id="time-left" className="text-center font-mono m-5">
            {Math.floor(timer / 60) < 10 ? "0" + Math.floor(timer/60): Math.floor(timer / 60)}:{seconds < 10 ? "0" + seconds : seconds}
            </div>
            <audio id="beep"></audio>
            <button onClick={handleStartStop} className="border-2 border-black p-1 rounded m-2" id="start_stop">{intervalOn? "Stop": "Start"}</button>
        </div>
    )
}
export default TimeLeft; 

My Session/Break Components:

import PropTypes from "prop-types"
const ControlTime = ({ time, setTime, type }) => {
  const maxTime = 60 * 60; 
    
  const incrementTime = () => {
    const newTime = time >= maxTime ? maxTime : time + 60;
    setTime(newTime)
    }
 
  const decrementTime = () => {
    const newTime = time <= 60 ? 60 : time - 60; 
    setTime(newTime) 
  }
  

    return (
      <div className="m-10 p-5 border-2 border-stone-400 rounded" id={`${type}-container`}>

        <h2 className="uppercase font-mono text-center" id={`${type}-label`}>{type}
          <p id={`${type}-length`}>{time/60}</p>
        </h2>

        <div className="flex flex-row m-1 px-2">
          <button className="hover:bg-stone-900 border-2 border-black hover:text-white rounded p-1 font-mono" id={`${type}-increment`} onClick={incrementTime}>{type} up</button>
          <button className="hover:bg-stone-900 border-2 border-black hover:text-white rounded p-1 font-mono" id={`${type}-decrement`} onClick={decrementTime}>{type} down</button>
        </div> 

      </div>
    )
}
ControlTime.propTypes = {
  time: PropTypes.number.isRequired, 
  setTime: PropTypes.func.isRequired,
  type: PropTypes.oneOf(["session", "break"]).isRequired
}
export default ControlTime;

I am fairly sure it’s an off-by-one in your state handling. Once timer is zero, the first useEffect() in StudyClock swaps the session types and resets the timers which I think prevents the zero time from rendering. Post a link to your project on codepen or the like and maybe someone can help debug further.