Problem with Pomodoro clock and resetting it

Hello! I’ve been stuck in this problem for about a week now, and even after hours of trying to solve it and trying a ton of things I haven’t been able to solve it.

I’m going to try to explain it the best I can.

The Start/Stop button works as expected. But when I try to reset the clock, I have to press it twice for the timer to restart properly when the clock is running.
I’ve tried so many things I wouldn’t be able to detail all of them, but the things I’ve considered are:

**-**Maybe I’m using timeout wrong. I notice that the clock keeps going down one second after I press the Reset button. I can only guess that the timeout is executing even after I press reset, so the function to decrease the timer is getting executed.

**-**I also considered I might be using UseEffect wrong. I read the documentation for UseEffect many times while trying to solve this issue but I’m still not sure what I’m handling wrong.

I’ve tried deleting the entire Timer component and writing it again, but I still get the same issue, so there’s an error in my logic probably.
I feel like I’ve hit a roadblock and I have no idea how to continue.

Here’s the CodeSandbox URL: pomodoro - CodeSandbox

Thanks! Any help or guidance is appreciated.

I don’t think you gave us the correct link to your project.

Ah, I’m very sorry! I pasted my code into CodeSandbox but it didn’t get saved.

It should work now: pomodoro - CodeSandbox

I looked at the code and from what I see is that you are making it so when you click the reset button it changes everything back to the default BUT what’s happening is that
this line
" setTimerDisp(convertTime(timer)); "
is setting the timer display to be what the current “timer” is (it doesn’t wait for the previous setTimer to complete).

This is why it works when you click reset twice (it first sets it the timer back to normal but the displayed timer goes off the old time and when you click again, the time got updated per previous reset click so it works"

EDIT: Ok so I double checked and there are a lot of dependencies with your code:
when one useEffect triggers, it triggers another one and another one and with the way setState works, it’s causing changes where you don’t want them.

You need to break the dependencies to be structured. It may seem like a small thing needs fixing but there is a lot going on under the hood causing your issue.

Thanks for your answer!
How could I begin to fix this? What do you mean with “structured”? Should I include everything in one UseEffect instead of creating many for different things?

Well one option you have that I tested and worked is to do this:

import useRef
create a useRef
Assign your your useRef to your setTimeout

useEffect(() => {
    console.log(timer,isRunning,int.current) //I used console log to test
    if (isRunning) {
      int.current = setTimeout(() => {
        setTimer(timer - 1);
      }, 1000);
  }, [isRunning, timer]);

In your other useEffect do this:

  useEffect(() => {
    setTimer(session * 60);
    console.log("test",timer)//again remove the console log
  }, [reset]);

key things are the clearTimeout because otherwise the function will just run even after your setState went through (and reverts it back to the time left -1)

Anyway, useEffect is a very powerful hook and should be used when you don’t want rerender but want to keep track of something. Very very useful.

Can explain in further detail if you want me to tomorrow so let me know if any questions.

1 Like

Thanks! I have never used UseRef so I will have to do further reading on it. I’ve read the documentation but I’m a bit confused.

Int is the name you chose for the ref, correct?
“useEffect is a very powerful hook and should be used when you don’t want rerender but want to keep track of something. Very very useful.”
Did you mean to write UseRef?

Thanks! (And sorry for the very late reply)

EDIT: Your fix worked, thanks! But when I started testing it I ran into a ton of problems, so I decided to simplify my code a bit and start all over again with a better understanding of the issues.

But… now I get multiple errors that only say “script error” or parts that say “there should be an element with an id=’…’” give me an error as if the element didn’t exist or something.
I’m not entirely sure what the reason could be, because everything seems to be working fine and I’ve put a time-left element, with the correct format, etc.
(I’ve put 50ms instead of 1000 just for testing purposes)

The link to my updated project is: Pomodoro2 - CodeSandbox

Yup int was the name I used - int just means for me the integer as in the interval ID. And yes also I mean useRef but useEffect is also powerful :smiley: !

I tried accessing your sandbox but I’m getting errors if I make any change. I think it’s a problem with your fcc test, why don’t you put it into your index.html file instead via a script tag?

it has very few downloads (I stay clear of low download counts on npm) and was last updated a year ago. I would remove it.

Can you mention which specific tests you are having troubles with?

Alright, changed the fcc test to External resources. It should work properly now I guess?

The tests I get the errors with are:

    1. I can see an element with corresponding id=“time-left”.


    1. When I click the element with the id of “reset”, any running timer should be stopped, the value within id=“break-length” should return to 5, the value within id=“session-length” should return to 25, and the element with id=“time-left” should reset to it’s default state.
    1. When I first click the element with id=“start_stop”, the timer should begin running from the value currently displayed in id=“session-length”, even if the value has been incremented or decremented from the original value of 25.

And the rest from the #Timer section starting from number 8.
(The Audio ones don’t pass either but that’s because I haven’t put the audio yet)

EDIT: Ah, the reason why I’m using react-fcctest instead of the external resource is because I previously asked here why I was getting an error and I was told to use that instead because it wasn’t working well.

I’m getting an error when editing code

e.transpileJobs[t].finally is not a function

Do you mean you had errors when putting this into the html?

Hmmm I think that’s a problem with CodeSandbox? I don’t get that error when I edit my code locally and I’ve always gotten that error while I’m editing code in CodeSandbox. I’m not sure what causes it though.

The error I get when I add the tests as external resource is in this post

Wait, why do you have two elements with the id of time-left?

Double check your ids and try to console log the timer to see if its synced properly

AH god. I didn’t catch that. You’re right. Thanks! I deleted the first wrongly placed time-left. Now it’s passing almost all tests EXCEPT for the 1st one in timer.
“25 + 5 has paused but time continued elapsing: expected ‘00’ to equal ‘49’”
I’m wondering if this once again is related to UseEffect/the order useState is executed.

It was also failing 8 but I realized it was because once session reached 60, 60*60 = 3600, and I’m calculating the number of seconds and minutes by using % 3600, so of course it ended up being ‘00’. I fixed it by adding an if in that special case.

Now I just gotta figure out what’s causing the error in the 1st test from the Timer section :thinking:
Thanks once again for your help!

Hard to tell without being able to edit code but did you try clearing interval in your reset function? Basically when reset button is pressed you should also be clearing the interval from running

Sadly that didn’t work. I think I know why this is happening though. I was reading how the tests are set up, and I think it compares the value of the displayed time when hitting reset, and aftewards (or at least that’s what I understood) (Link to the tests)

By using console.log I realized that the displayed time has a “”“delay”"" (don’t know how else to call it). For example, here the timer has gone down to 1499, but the timer still displays the displayed time as 25:00, for some reason.

          '25 + 5 has paused but time continued elapsing'

My guess is that right when it presses reset, it’s getting that value with a “delay”, and afterwards it’s getting the new updated value post-reset, so they’re obviously not the same. I first tried moving the updateDisplay() function to the setInterval, because I thought that’d make sure both get updated at the same time, but the same thing happens. But yeah for some reason 25:00 gets repeated twice in my console (when it’s 1500s, and when it goes down to 1499s)

I’m really really confused right now lol this has turned into a headache. Again I’m just speculating, honestly I’m very lost as this point. Everything seems to be working just fine visually but it doesn’t pass the test. When I reset the timer it stops running too and everything… I don’t know.

Edit: Okay. Another thing that I tried was moving the display state to the parent where I have the reset function, and using setDisplay() when the reset button is pressed. That didn’t solve it, so now I’m not entirely sure if that’s the issue.
The error I get is “25 + 5 has paused but time continued elapsing: expected ‘00’ to equal ‘58’”. I’m not sure why it’s getting 58 seconds as last value though. I’ve used console.log to print the value in the display and I don’t think I’ve seen any values with 58 seconds that get displayed during that test in particular.

try setDisplay("25:00") in your reset function worked for me

basically what’s happening is that because setState is async it’s all happening at once and so when you’re running the convertTime function, it’s going based off the current time (it isn’t waiting for setTimer(1500); to be updated

Check this article setState is an asynchronous function | Sentry for a better explanation (it’s based of class based components but same concept applies regarding state updates)

1 Like

That worked! I guess I’ll have to do some more reading about setState. Seems like I always get something wrong related to it.

Much thanks! And thanks for the patience too. Everything is working now. All that’s left is adding the audio.

1 Like