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:
- When
alert
is updated,useEffect
is called. -
useEffect
checks ifalert
is true. -
If true,
setTimeout
is called andalert
is set tofalse
after 3 seconds. - The next time the
useEffect
is called andalert
istrue
,clearTimeout
is called and clears the previoussetTimeout
before calling a newsetTimeout
. (Ifalert
isfalse
,clearTimeout
isn’t called, butsetTimeout
isn’t called either, so I figured there wouldn’t be a problem where multiplesetTimeout
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:)