React functional component setState not updating the state

I am building a Pomodoro clock as described in Free Code Camp curriculum here.

Code is available here.

In Timer component, isSessionMode state which holds a boolean value is toggled conditionally in startTimer method (line: 40) as below.

setIsSessionMode((prev) => !prev);

But the state doesn’t seem to be updated because the logger in useEffect hook doesn’t print the state (line: 19) after setIsSessionMode() is called as below.

useEffect(() => {
      console.log(isSessionMode)
      if (isFirstRun.current) {
        isFirstRun.current = false;
        return;
      }
      audioRef.current.play();
    }, [isSessionMode, audioRef]);

And also the state dependent log in return statement on line: 74 doesn’t print value as expected according to the updated state as below.

{isSessionMode ? "Session" : "Break"}

Why isSessionMode is not getting updated?

Also, it seems that when updating the isSessionMode state with setSessionMode() , a warning is thrown as follows:

Warning: Cannot update a component (`Timer`) while rendering a different component (`App`).

I searched around the web regarding it a bit but is unable to follow.

I haven’t run your code, but I notice this:

          setIsSessionMode((prev) => !prev);

I think you are confusing setState and useState. The hook works differently than the class method. With the setter returned by useState (in this case setIsSessionMode) you just tell it what you want the new isSessionMode to be. If you are toggling, I would expect to see something like:

          setIsSessionMode(!isSessionMode);

Passing an updater to the dispatcher is a valid way of updating state. It is even more preferable if you are updating state based on the previous one.

Again, I think you’re confusing setState with useState, What you are saying makes complete sense if we’re talking about setState. I have never heard of what you’re saying about a useState setter. I can’t find an example in the React docs.

I take that back - it looks like that does exist for useState too. Hmmm.

1 Like

You shouldn’t pass state updater function as a prop (e.g. setTimerLength from App to Timer). Create a separate function in App that calls state updater and pass it as a prop.
:flushed:

I notice that if I put a timer where the setIsSessionMode is being called, it is being re-called in a few milliseconds. Perhaps the component doesn’t rerender in time. I notice that when I do setIsSessionMode(!isSessionMode);, that it does rerender before it is toggled back. So, perhaps the callback function is taking long enough for the useEffect to not catch it.

But if you know that you want that effect to happen, why not just call a function that does what you want there instead of setting a flag that get’s listened for that my not fire off in time?

Do you have documentation for that? I do that all the time.

I also notice that as I run your stuff and put a log in the callback to setInterval, things are being called twice, almost like two intervals are being set. I haven’t been able to figure out why.

Ok, I take that back too :slight_smile:

Looks like there is a note in React docs regarding state updating function (which I haven’t seen before :man_shrugging: ):

Note

React guarantees that setState function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.

1 Like

That’s strange, setIsSessionMode(!isSessionMode) seems to work but setState with callback syntax does not. Don’t understand why.

In fact I also have same app built in codepen [here] which I built earlier (https://codepen.io/abhi747/pen/eYZNbpb), where the setState with callback syntax is working perfectly fine.

I am just incorporating the app in codesandbox, to segregate different components in different files as it is not possible to create files in codepen.

I need to have state flag (isSessionMode) to conditionally render session mode title in the UI i.e, ‘Session’ or ‘Break’ .

Can you please explain the reason for this approach instead of passing state updater directly as prop?

I was wrong. Look at my reply to kevinSmith a couple posts earlier :flushed:

Sorry, I missed your reply earlier. :slightly_smiling_face: