0

I am building a simple timer app and keeping track of elapsed time in state using hooks. I know I am setting state correctly because it displays in the app every passing second. However, when I console log elapsedTime, it repeatedly logs the initial state (0 in this case):

const Timer = () => {
  const [elapsedTime, setElapsedTime] = React.useState(0);
  const [totalTime, setTotalTime] = React.useState(0);

  const handleStart = () => {
    const startTime = Date.now();
    setInterval(() => {
      const et = Math.floor((Date.now() - startTime) / 1000);
      setElapsedTime(et);
      console.log(elapsedTime);
    }, 1000);
  };

  const handleStop = () => {
    clearInterval();
  };

  return (
    <div className='container'>
      <div className='timer'>
        <div className='title'></div>
        <div className='time'>{elapsedTime}</div>
        <button onClick={handleStart}>Start</button>
        <button onClick={handleStop}>Stop</button>
      </div>
    </div>
  );
};

ReactDOM.render(<Timer />, document.getElementById("root"));
<div id="root"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

Why is my change in state not being reflected in the console.log I call on line 9?

Janez Kuhar
  • 3,705
  • 4
  • 22
  • 45
Casey Cling
  • 401
  • 2
  • 5
  • 15
  • Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Janez Kuhar May 18 '21 at 19:05
  • *Aside:* Your code doesn't stop the timer. And weird things start to happen when one tries to start another timer while one is already active. :) – Janez Kuhar May 18 '21 at 19:11
  • Yeah I know, I'm trying to figure that out right now lol how would you go about stopping the timer? – Casey Cling May 18 '21 at 19:41
  • 1
    As a general rule, try to avoid adding questions to an existing one. If you encounter another problem, ask a new question. – Janez Kuhar May 18 '21 at 20:01

3 Answers3

2

setState is asynchronous, you won't see the changes in the scope of the function

If you want to see the new values at the place where you put the console.log you can only do console.log(et);

You can also use a useEffect to see the changes of the variable in the console

useEffect(() => {
  console.log("elapsedTime", elapsedTime);
}, [elapsedTime]);
VersifiXion
  • 2,152
  • 5
  • 22
  • 40
  • Something I use all the time is: console.log({elapsedTime}); Try it out, you will love it :D No need to write the variable name in clear text when logging it. – hellogoodnight May 20 '21 at 04:43
1

Because of javascript clojure, elapsedTime on line 9 will always reference the value it had when the function was created.

hellogoodnight
  • 1,989
  • 7
  • 29
  • 57
0

how would you go about stopping the timer?

You have to have a reference to the timer you're creating so that you can stop it. A call to setInverval returns such a reference (a unique ID).

Then you have to make sure you only have a single timer running at any point in time.

Borrowing Marc Charpentier's answer, your working version of the code could look like this:

const Timer = () => {
  const [elapsedTime, setElapsedTime] = React.useState(0);
  const [totalTime, setTotalTime] = React.useState(0);
  const [timerId, setTimerId] = React.useState();

  React.useEffect(() => {
    console.log("elapsedTime", elapsedTime);
  }, [elapsedTime]);

  const handleStart = () => {
    const startTime = Date.now();
    if (timerId === undefined) { // make sure that a timer is not already running
      setTimerId(
        setInterval(() => {
          const et = Math.floor((Date.now() - startTime) / 1000);
          setElapsedTime(et);
        }, 1000)
      );
    }
  };

  const handleStop = () => {
    clearInterval(timerId);
    setTimerId(undefined);
  };

  return (
    <div className='container'>
      <div className='timer'>
        <div className='title'></div>
        <div className='time'>{elapsedTime}</div>
        <button onClick={handleStart}>Start</button>
        <button onClick={handleStop}>Stop</button>
      </div>
    </div>
  );
};

ReactDOM.render(<Timer />, document.getElementById("root"));
<div id="root"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
Janez Kuhar
  • 3,705
  • 4
  • 22
  • 45