React useEffect Cleanup Function within If Statement

Hello. Hope everyone is having a good day.

I am going through ‘project 9’ of Code 15 React Projects - Complete Course, and I have a question regarding useEffect cleanup functions.

In the tutorial, we have a state variable alert that holds a boolean value (true or false). When the user clicks the desired color, onClick is called and sets alert to true. Then, we use conditional rendering to show the text ‘copied to clipboard’ which is displayed when alert is true.

Then, to stop displaying the ‘copied to clipboard’ text, we utilize a useEffect that calls setTimeout to set alert back to false after a 3-second delay. A cleanup function, clearTimeout, is called within the return statement, so that the next time useEffect is called, the previous setTimeout (stored in the variable timeout) is cleared before calling a new setTimeout.

Here is the useEffect code from the YouTube tutorial (with a console.log statement I inserted to test when the useEffect was being called):

  useEffect(() => {
      const timeout = setTimeout(() => {
        setAlert(false);
      }, 3000);
      console.log('useEffect called');
      return () => clearTimeout(timeout);
  }, [alert]);

However, due to the way useEffect is set up, setTimeout is unnecessarily called when the components are first rendered and state variables are initialized. A total of 21 times, because there are 21 <SingleColor /> components that each initialize an alert state variable.

I only wanted setTimeout to be called when alert is true, which equates to when the specific color is clicked. So, I rewrote the code as below (everything else is the same as the YouTube tutorial):

useEffect(() => {
    if (alert) {
      const timeout = setTimeout(() => {
        setAlert(false);
      }, 3000);
      return () => clearTimeout(timeout);
    }
  }, [alert]);

From my understanding, this is how the new useEffect works:

  1. When alert is updated, useEffect is called.
  2. useEffect checks if alert is true.
  3. If true, setTimeout is called and alert is set to false after 3 seconds.
  4. The next time the useEffect is called and alert is true, clearTimeout is called and clears the previous setTimeout before calling a new setTimeout. (If alert is false, clearTimeout isn’t called, but setTimeout isn’t called either, so I figured there wouldn’t be a problem where multiple setTimeout methods at the same time.

I was wondering if my understanding was correct, because this is the first time I put a cleanup function within an if block. The code seems to work, but I don’t know how to check if the previous setTimeout is being cleared successfully and want to make sure there are no bugs.

Also, I have a separate question. When using the original code (without the if block), I was wondering how it didn’t lead to an infinite loop of useEffect being called.

Here is the original code again:

useEffect(() => {
      const timeout = setTimeout(() => {
        setAlert(false);
      }, 3000);
      console.log('useEffect called');
      return () => clearTimeout(timeout);
  }, [alert]);

Since this useEffect is being called every time the alert state is updated, doesn’t setting alert to false trigger the same useEffect to be called again, which again sets alert to false, which triggers useEffect, and so on?

Thank you so much! All help is immensely appreciated:)