A Comprehensive Guide to Cleanups in useEffect: Simplified

A Comprehensive Guide to Cleanups in useEffect: Simplified

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:

  1. Render a button with an onClick that toggles an active state

  2. Reuse the counter from the previous example and send the count as prop to the component with the button

  3. Whenever active is true and any key is pressed on the keyboard, the key code of the pressed key is printed in the console.

  4. 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!