0

I am a starter at React! Started last week ;)

My first project is to create a timer which has a reset function and a second count function.

The reset function is working great, however the timer does not. Which is the best way to do it? It should increase +1s on variable 'second' according to the setTimeout() function.

Is it possible to create a loop on Hooks? I tried to do with the code below, but the page goes down, I think it is because the infinite loop that the code creates;

    const [hour, setHour] = useState(4)
    const [minute, setMinute] = useState(8)
    const [second, setSecond] = useState(12)

    // methods
    const setTime = (value: string) => {

        if (value === 'reset'){
            setHour(0);
            setMinute(0);
            setSecond(0);
        } 
    }

    const startTime = () => {
        while (second < 60){
            setTimeout(() => {
                setSecond(second + 1);
            }, 1000);
        }
    };

    <div className="d-flex justify-content-center">
         <MainButton 
             variantButton="outline-danger"
             textButton="RESET"
             functionButton={() => setTime('reset')}
     />
         <MainButton 
             variantButton="outline-success"
             textButton="START"
             functionButton={() => startTime()}
          /> 
     </div>
João Denari
  • 111
  • 1
  • 7
  • Does this answer your question? [Countdown timer in React](https://stackoverflow.com/questions/40885923/countdown-timer-in-react) – Emile Bergeron Feb 07 '23 at 15:54

1 Answers1

1

Welcome to React! You're very close. setTimeout and setInterval are very similar and for this you can simply use setInterval. No need for a while() loop! Check out this working Sandbox where I created a simple React Hook that you can use in your App.js

https://codesandbox.io/s/recursing-hooks-jc6w3v

The reason your code got caught in an infinite loop is because startTime() function has stale props. Specifically, the second variable is always 0 in this case, because when you defined startTime() on component mount, second was 0. The function doesn't track it's incrementing.

To resolve this issue, instead of:

setSecond(second + 1);

Try using:

setSecond((s) => s += 1);

EDIT* There are many good articles on React Stale Props. Here's one that's helpful: https://css-tricks.com/dealing-with-stale-props-and-states-in-reacts-functional-components/

EDIT** Additional inline examples of the exact issue:

Two changes I would make:

  1. Use setInterval instead of setTimeout in a while() loop.
  2. Create a useTimer hook which handles your timer logic.

App.js

import "./styles.css";
import useTimer from "./useTimer";

export default function App() {
  const [setTime, startTime, stopTime, hour, minute, second] = useTimer();
  return (
    <div>
      <div className="d-flex justify-content-center">
        <button onClick={() => setTime("reset")}>RESET</button>
        <button onClick={startTime}>START</button>
        <button onClick={stopTime}>STOP</button>
      </div>
      <br />
      <div>
        Hour: {hour} <br />
        Minute: {minute} <br />
        Second: {second} <br />
      </div>
    </div>
  );
}

useTimer.js

import { useState } from "react";

const useTimer = () => {
  const [hour, setHour] = useState(4);
  const [minute, setMinute] = useState(8);
  const [second, setSecond] = useState(12);
  const [timer, setTimer] = useState();

  // methods
  const clearTimer = () => clearInterval(timer);

  const setTime = (value) => {
    if (value === "reset") {
      setHour(0);
      setMinute(0);
      setSecond(0);
    }
  };

  const startTime = () => {
    if (timer) clearTimer();
    const newInterval = setInterval(() => {
      setSecond((s) => (s += 1));
    }, 1000);
    setTimer(newInterval);
  };

  const stopTime = () => clearTimer();

  return [setTime, startTime, stopTime, hour, minute, second];
};

export default useTimer;
Wesley LeMahieu
  • 2,296
  • 1
  • 12
  • 8