In my first React interview, I messed up big time because I did not understand the concept of cleanup in React useEffect
hook. So, in this blog, we will take a deep look at it with the help of an example.
Let's start!
What is useEffect
?
A useEffect
is a hook in React that runs the code inside it whenever the dependencies that we provide to it change.
Let's look at a basic example:
More complex example
Not let's look at a more complex example. Let's do something similar to the problem statement given to me during the interview.
Here are the requirements we will implement:
Render a button with an
onClick
that toggles anactive
stateReuse the counter from the previous example and send the count as
prop
to the component with the buttonWhenever
active
is true and any key is pressed on the keyboard, the key code of the pressed key is printed in the console.The key code should be printed as many times as the
count
prop
Let's start implementing!
If you know basic React, then the first two should be easily implementable, like this:
Now, let's add the key-down event listener in a useEffect
.
Initially, this seems to be working fine. There are no console logs initially when active
is false
, but once we toggle it to true
, there were console logs and they were correct too.
The bug comes when we toggle active
to false
once more. There should be no console logs now but the logs are still working like when active
was true
.
The problem becomes worse when we add the count prop to the mix:
When count is 1
, it seems to be working fine, but once we update the count to anything more, the values add up. When count is 2
, the logs are printed 3 times. When count is 3
, the logs are printed 6 times.
Why do you think this is happening? Let's add console logs in the keyDownHandler
function to debug.
Here's the result:
As you can see, it seems like the event handlers are being stacked up. Individually, they are working as expected, but every time either count
or active
changes, a new event handler is registered with the values of active
and count
locked in for that particular handler.
P.S.: The reason why active
and count
don't change in their respective event handlers due to a concept called closures. You can learn more about it here.
This causes multiple event listeners to be fired every time we press a key on our keyboard.
Fixing the bug by using useEffect
cleanup
Fixing the bug is super simple! All we have to do is make sure that the previous event handler is removed every time we want a new event handler, i.e., every time count
or active
changes.
This can be done by returning a cleanup function from useEffect
. This cleanup function is run whenever the dependencies change before the new useEffect
is run.
Let's fix our bug and see it in action:
As you can see, every time active
or count
change, the cleanup function is run. It removes the previous event handler and so, only one event handler is present on window
for the keydown
event at any given time.
This is how our app works finally, exactly like we want it to:
Conclusion
In conclusion, understanding cleanups in the useEffect
hook is essential for managing side effects in React applications. By returning a cleanup function from useEffect
, you can ensure that previous event handlers are removed before new ones are added, preventing unwanted behaviour and improving overall application performance.
This is a part of my series about important but overlooked topics in React. Do check out the other parts as well, and follow for more!