Etep
Etep

Reputation: 3601

Pomodoro Clock (using React Hooks) countdown does not start

Tried to refactor this Pomodoro Clock to use React Hooks. Ran into an issue with the countdown not starting when the Start Timer button is clicked. I think the issue comes from the intervalRef.

Am I using the useRef correctly?

https://codesandbox.io/s/k95zk32897

If you don't feel like going to sandbox:

import React, {useState, useRef } from 'react'

export default function Pomodoro() {
    const [seconds, setSeconds] = useState(0)
    const [workMinutes, setWorkMinutes] = useState(25)
    const [restMinutes, setRestMinutes] = useState(5)
    const [start, setStart] = useState(false)
    const [relax, setRelax] = useState(false)

    // Control functions
    const intervalRef = useRef();

    function startTimer() {
        if(!intervalRef.current){
            intervalRef.current = setInterval(1000)
        }
        setStart(!start)
    }
    function pauseTimer() {
        if(!intervalRef.current){
            clearInterval(intervalRef.current)
            intervalRef.current = undefined
        }
    }
    function resetTimer() {
        if(!intervalRef.current){
            clearInterval(intervalRef.current)
            intervalRef.current = undefined
        }
        setSeconds(0)
        setWorkMinutes(25)
        setRestMinutes(5)
        setStart(false)
        setRelax(false)
    }
    function timer() {
        if(seconds === 0){
            setSeconds(59)
        }else{
            setSeconds(seconds - 1)
        }

        if(relax) {
            if(seconds === 0){
                setRestMinutes(restMinutes - 1)
            }
            else if(restMinutes === 5){
                setRestMinutes(4)
            }else{
                setRestMinutes(restMinutes)
            }
        }

        if(restMinutes === -1) {
            setRestMinutes(5)
            setRelax(false)
        } else {
            setWorkMinutes( seconds === 0 ? workMinutes - 1 : workMinutes === 25 ? 24 : workMinutes)

            if (workMinutes === -1) {
                setWorkMinutes(25)
                setRelax(true)
            }
        }
    }

    return (
        <>
            {timer}
            <p>{relax ? 'Take a Break' : 'Get Busy'}</p>
            <p>{relax ? restMinutes : workMinutes} : {seconds < 10 ? `0${seconds}` : seconds}</p>
            <button onClick={start ? pauseTimer: startTimer}>{start ? 'Pause' : 'Start'}</button>
            <button onClick={resetTimer}>Reset</button>
        </>
    )
}

Upvotes: 0

Views: 587

Answers (1)

r g
r g

Reputation: 3901

You forgot about callback in setInterval, but i would rather do it like that anyway:

import React, { useState, useEffect } from "react";

export default function Pomodoro() {
  const [seconds, setSeconds] = useState(25 * 60);
  const [paused, setPaused] = useState(true);

  useEffect(() => {
    const int = setInterval(() => {
      console.log(`${Date.now()} - paused: ${paused}`);
      if (!paused) {
        setSeconds(s => s - 1);
      }
    }, 1000);
    return () => {
      clearInterval(int);
    };
  }, [paused]);

  function startTimer() {
    setPaused(false);
  }
  function pauseTimer() {
    setPaused(true);
  }
  function resetTimer() {
    setPaused(true);
    setSeconds(25 * 60);
  }

  return (
    <>
      {`${Math.floor(seconds / 60)}:${("00" + (seconds % 60)).slice(-2)}`}
      <button onClick={paused ? startTimer : pauseTimer}>
        {paused ? "Start" : "Pause"}
      </button>
      <button onClick={resetTimer}>Reset</button>
    </>
  );
}

Sandbox: https://codesandbox.io/s/k9j70jrjy5

You dont really need so many states - they are depending of each other so you can simplify the logic (of course you can do one more for work/relax feature which i didn't bother).

You handle relax timer like that, by just starting with 10min instead of 25min:

  function startRelaxTimer() {
    setSeconds(10 * 60);
  }

or by handling it in another states but then you need to do everything twice (is fine only if you care about remembering old state when switching).

const [relax, setRelax] = useState(false);
const [relaxSeconds, setRelaxSeconds] = useState(10 * 60);
useEffect(() => {
    const int = setInterval(() => {
      console.log(`${Date.now()} - paused: ${paused}`);
      if (!paused) {
        relax ? setRelaxSecdonds(s => s - 1) : setSeconds(s => s - 1);
      }
    }, 1000);
    return () => {
      clearInterval(int);
    };
  }, [paused, relax]);

Upvotes: 2

Related Questions