Zeddrix Fabian
Zeddrix Fabian

Reputation: 2566

Fully functioning countdown timer using React hooks only

Here's my code. You can also check it out on Stackblitz:

import React, { useState } from 'react';

const Timer = ({
    initialHours = 10,
    initialMinutes = 0,
    initialSeconds = 0,
}) => {
    const [hours, setHours] = useState(initialHours);
    const [minutes, setMinutes] = useState(initialMinutes);
    const [seconds, setSeconds] = useState(initialSeconds);

    let myInterval;

    const startTimer = () => {
        myInterval = setInterval(() => {
            if (seconds > 0) {
                setSeconds(seconds - 1);
            }
            if (seconds === 0) {
                if (hours === 0 && minutes === 0) {
                    clearInterval(myInterval);
                } else if (minutes > 0) {
                    setMinutes(minutes - 1);
                    setSeconds(59);
                } else if (hours > 0) {
                    setHours(hours - 1);
                    setMinutes(59);
                    setSeconds(59);
                }
            }
        }, 1000);
        cancelTimer();
    };
    const cancelTimer = () => {
        return () => {
            clearInterval(myInterval);
        };
    };

    return (
        <div>
            <h1 className='timer'>
                {hours < 10 && hours !== 0 ? `0${hours}:` : hours >= 10 && `${hours}:`}
                {minutes < 10 ? `0${minutes}` : minutes}:
                {seconds < 10 ? `0${seconds}` : seconds}
            </h1>
            <button onClick={startTimer}>START</button>
            <button>PAUSE</button>
            <button>RESUME</button>
            <button onClick={cancelTimer}>CANCEL</button>
        </div>
    );
};

export default Timer;

I'm having trouble with the START button and this is what it looks like when I click on the START button multiple times: enter image description here

You'll notice that on the first click the number never continues to go down unless I click on the START button again and again but it would look like a broken slot machine. And if I hit on the CANCEL button, it should stop the timer and reset back to the set time, but it doesn't. I don't know how to solve the problem for this 2 buttons, and much more for the PAUSE and RESUME. I don't know how to make them work, too. Please help.

Upvotes: 2

Views: 1439

Answers (1)

Abhay Sehgal
Abhay Sehgal

Reputation: 1723

As suggested by @Felix Kling you can try a different approach, to check why your code is not working check the below code, I've made some changes in your Timer component :

import React, { useState } from 'react';

const Timer = ({
    initialHours = 10,
    initialMinutes = 0,
    initialSeconds = 0,
}) => {
    const [time, setTime] = useState({
        h: initialHours,
        m: initialMinutes,
        s: initialSeconds,
    });

    const [timer, setTimer] = useState(null);

    const startTimer = () => {
        let myInterval = setInterval(() => {
            setTime((time) => {
                const updatedTime = { ...time };
                if (time.s > 0) {
                    updatedTime.s--;
                }

                if (time.s === 0) {
                    if (time.h === 0 && time.m === 0) {
                        clearInterval(myInterval);
                    } else if (time.m > 0) {
                        updatedTime.m--;
                        updatedTime.s = 59;
                    } else if (updatedTime.h > 0) {
                        updatedTime.h--;
                        updatedTime.m = 59;
                        updatedTime.s = 59;
                    }
                }

                return updatedTime;
            });
        }, 1000);
        setTimer(myInterval);
    };

    const pauseTimer = () => {
        clearInterval(timer);
    };

    const cancelTimer = () => {
        clearInterval(timer);
        setTime({
            h: initialHours,
            m: initialMinutes,
            s: initialSeconds,
        });
    };

    return (
        <div>
            <h1 className='timer'>
                {time.h < 10 && time.h !== 0
                    ? `0${time.h}:`
                    : time.h >= 10 && `${time.h}:`}
                {time.m < 10 ? `0${time.m}` : time.m}:
                {time.s < 10 ? `0${time.s}` : time.s}
            </h1>
            <button onClick={startTimer}>START</button>
            <button onClick={pauseTimer}>PAUSE</button>
            <button onClick={cancelTimer}>CANCEL</button>
        </div>
    );
};

export default Timer;
    

Explanation:

  1. in your startTimer function in the last line you're calling cancelTimer
  2. When you're working with hooks then keep in mind you won't get updated value of state variable until you use function inside a set function like I'm doing in setTime and in that callback, you'll get an updated value as a first parameter
  3. In cancelTimer method you're returning a function you've to call clearInterval also myInterval is undefined in cancelTimer so I've set it's value in state

For more information and other ways check this question

Upvotes: 2

Related Questions