FAQ: The Effect Hook - Control When Effects Are Called

Thank you for your response. It’s clearer now

1 Like

Hello,
Thank you for these very interesting explanations!
Can you please just explain why in step 3, the existing interval continues to be cleared and the clock seems to stop, while time was updated by setTIme just before the interval was cleared?
Thank you

When we get to Step 3, our useEffect looks like:

useEffect(() => {
    const intervalId = setInterval(() => {
      setTime((prev) => prev + 1);
    }, 1000);

    return () => {
      clearInterval(intervalId);
    };
  });

When the component is rendered for the very first time, the following happens:

  • the callback function is executed. In this particular case, the function will set up an interval through setInterval. Since we have specified the delay as 1000 milliseconds (or 1 second). the setInterval will not call the state setter setTime till one second has elapsed.

  • the cleanup function is not executed, but useEffect is made aware that if the component is re-rendered or unmounted, then the cleanup function must be executed before re-rendering/unmounting.

If the time changes or some text is typed into the input field, then the component is re-rendered. When the component is re-rendered, the following happens:

  • the cleanup function is executed. This clears the interval that was set the previous time the useEffect was called.

  • After the cleanup function has been executed, then the effect

const intervalId = setInterval(() => {...}, 1000);

is carried out.

--- Every time the useEffect is triggered for a re-render, the cleanup function is executed first and then the actual effect is run.

--- If the component is to be unmounted, then only the cleanup function is executed (the effect is not run as the component is to be unmounted).

If we change the text in the input field, useEffect is triggered. The cleanup function is run first and it clears the existing interval (regardless of whether one second has passed or not). Then a new interval is set. As we keep typing in the input field, the interval keeps getting cleared and the clock is stuck. If we stop typing for at least a second, then the clock advances by a second.

In Step 4, this is rectified by using the empty dependency array for the useEffect. The empty dependency array means that the effect (of setting an interval) will only happen the very first time the component is rendered. The cleanup function will only be executed when the component is unmounted. If the state changes (because of time being incremented OR text being changed in the input field), then the component will be re-rendered. But, because of the empty dependency array, the useEffect will not be triggered. Neither the cleanup function will be executed nor a new effect will happen in response to any re-renders. Even though the useEffect is not triggered for re-renders, the clock keeps advancing after every second (as we wanted) because the original interval set by setInterval keeps calling setTime after every second.

If I have misunderstood your question, share your thoughts and elaborate a bit more on what you find confusing.

Thank you for your answer, it is very clear!
I didn’t understand that every time the useEffect is triggered for a new rendering, the cleanup function is executed first, and only after that the actual effect is executed!
Thanks again for your answer.

1 Like

Can anyone possibly explain why we use an Effect hook with an empty dependency array to call the effect once at the beginning of a render instead of just calling a separate function once with the same ‘effect’ and just not using the effect hook altogether?

I think that, you would have no way to deal with the issue of adding an additional setInterval() on each render. If you add the setInterval() and clearInterval() outside of useEffect(), then an interval is started, and immediately stopped, and the timer never increases. If you don’t include the clear, then it keeps going up by the number of renders that have occurred, instead of just 1. useEffect() with the empty dependencies is basically saying, execute this code on the very first render, but then bypass it altogether on any subsequent ones.

Thank you for this, helped a lot !!

1 Like

I had a similar question, so I’m posting what the solution is in case someone else has this question too. It’s been over a year since the OP asked the question, so not expecting to help them.

Instead of passing in the returned values of these functions, we need to pass in the functions themselves. The anonymous function/fat arrow syntax allows us to pass a function rather that the returned value of that function.

For example, if I want to use a sum() function with setInterval and I don’t use the anonymous function syntax, this is what happens.

setInterval(sum(2, 3), 1000);

resolves to

setInterval(5, 1000);

which is not what we want. We want to pass in the function as the argument.

setInterval(() => sum(2, 3), 1000);

This one doesn’t resolve to 5; it remains as a function. The setInterval will call that function every so often.

EDIT: This was answered later in the thread but I guess it’s helpful to have this as a reply for the first time it is asked.

1 Like

Question for y’all. Why are the curly braces necessary for the anonymous function in setInterval? In other words why does the lesson accept this:

useEffect(() => {
  setInterval(() => {
    setTime((prev) => prev + 1);
  }, 1000);
});

And not this:

useEffect(() => {
  setInterval(() => setTime((prev) => prev + 1), 1000);
});

It took me a while to figure out what I was doing wrong.

1 Like

Hi there!
It’s might be off-topic but it’s not the first time seeing this target “variable” in functions parameter. For example from this lesson.

const handleChange = ({ target }) => setName(target.value);

I tried to write this function by myself but then I have few questions.

  1. What is target?
  2. Why I need to put it in curly brackets ( i mean this: {} )?
  3. Why I don’t need to put the functions body ( setName(target.value); ) to curly brackets?

Thank you for your the answers!

1 Like

target refers to the property of the event: Event: target property - Web APIs | MDN
whereas the ({ target }) => syntax is being used for “Object Destructuring”).

For the first two questions, see this post:
https://discuss.codecademy.com/t/faq-the-state-hook-use-state-setter-outside-of-jsx/535825/6

If you still find something confusing, share your thoughts.

In this particular case, omitting or including the curly braces won’t make much of a difference. If you want to include curly braces for the body of the function, you can do so. The setName state setter is going to set a new value for name, so name will be updated. But the setName function doesn’t return any value explicitly. So the return value of the function call setName(target.value) will be undefined. Whether we choose to wrap the body of the arrow function in curly braces or choose to omit them, it isn’t going to make a difference in this particular case. However, if a value other than undefined was being returned from the body of the arrow function, then omission/inclusion of curly braces is important. In that case, if we chose to wrap the body in curly braces, then we must explicitly use the return keyword to ensure that the correct value is returned.

2 Likes

Your solution works because it already contains the empty dependency array. I believe they wanted us to solve the problem of the multiple setIntervals by using a returning cleanup function first, but then finding that there are still problems when other rerenders are triggered by the input element. And then finally we sort it all out by using the empty dependency array, so that only one setInterval is created.