Long Live Unmount!
Recently, I encountered an app that was behaving very poorly, being slow and unresponsive, which it shouldn't have been. I discovered a flaw in a component that was using many timed animations. This prompted me to write this post before fixing the issue in that component :P.
React apps are SPAs (Single Page Applications), which render the content of a component without reloading the entire page. Unmounting is the process of leaving one component and moving to another, similar to navigating from one link to another within an app.
Why Unmounting?
Suppose, you are on a Vanilla JS application and you want to navigate from “About” Page to “Contact” Page (Let’s assume these are static pages), what would be the steps?
We are currently on “About” Page.
We find the “Contact” Page and click it.
Finally, on “Contact” Page. Phew!
Similar steps will be performed in React as well. To navigate, we will be using React Router DOM. Instead of pages, we will be having components and moving from one to another.
As we have read, React provides an Unmounting process. In simpler words, React tracks whenever you left a component. But why? What’s the need? Let’s find out.
Suppose, I have setup a navigation bar with “Header Component” having About and Contact Links to it (Assuming you have a setup for React Router DOM) which looks like below.
const Header = () => {
return (
<div className='header'>
<div className='nav-items'>
<ul>
<li>
<Link to="/">Home</Link></li>
<li>
<Link to="/about">About Us</Link></li>
<li>
<Link to="/contact">Contact Us</Link></li>
</ul>
</div>
</div>
)
}
export default Header;
Create a Simple “About” Component like below -
import React, {useEffect} from "react";
const About = () => {
useEffect(() => {
setInterval(() => {
console.log("I am a timer from About Component");
},1000)
},[])
return (
<div>
<h1>Hello! I am About Section</h1>
</div>
)
}
export default About;
In this component, we have added a simple setInterval in useEffect which logs the “I am a timer from About Component” every second.
Create a simple “Contact” component (without any interval/timer).
const Contact = () => {
return (
<h2>Hello! I am Contact Component</h2>
)
}
export default Contact;
Now, run your React app, click on “About” Link and open you console.
So, the timer starts which is expected. Another expectation is, when we move to “Contact” component, this shouldn’t be existing any more, right 🙃? Let’s click on “Contact” page and see the output -
🧐 Confused? The log on console should go away. But it still logs and after every second, it’s increasing the counter as well. OK, No worries, maybe a GLITCH 🤓. Do one thing, click on “About” Again, AND
WATCH CAREFULLY! Console log is still there, but counter starts increasing by 2 every second 🤯. Go back to “Contact”, and again back to “About” Page would make the counter increasing 3 times every second. MY GOD! REACT IS CRAZY.
BEHIND THE SCENES
We have been reading in this article, that React works on components rendering rather than full page reloading, which is the reason behind this happening.
Because React apps are Single Page Applications, so everything is happening on a ONE page only. It’s only rendering or loading the content of the component. If it would be reloading the full page (maybe we don’t require React then), this wouldn’t be happening. Consider a STACK scenario, where you are appending items and not actually removing them.
When we move from “About” to “Contact” page, we just moved from the component but the setInterval still exists. It was never told to disappear. Similarly, when you keep navigating, it’s the amount of times you are moving on “About” page, the counter keeps increasing that many.
Imagine an app, where a carousel is used to change the photo after every 5th second. A user visited the app, keep navigating here and there. If we do not have any process to control it, then it will keep rendering it that many times and suddenly our UI breaks. HERE, we have “UNMOUNTING” to the rescue. Below code shows an example -
import React, {useEffect} from "react";
const About = () => {
useEffect(() => {
let timer = setInterval(() => {
console.log("I am a timer from About Component");
},1000)
return () => {
clearInterval(timer);
}
},[])
return (
<div>
<h1>Hello! I am About Section</h1>
</div>
)
}
export default About;
Here, we have stored the reference of setInterval inside a variable and at the time of unmounting (when we leave “About” Component), we are clearing it. Now, if you move from “About” to “Contact” Component, the counter stops. WOAHHH! And the Reason why React tracks when we leave a Component 😉.
From now on, make sure to unmount any cases where timers or animations are used. This way, they won't keep running in the background, and not to send us in a “Blue Hell” where we keep debugging in our logics/states/components. In more technical terms, Unmounting removes the component from the app's UI and cleans up its resources to prevent memory leaks.
I hope you like this article and let me get back to my project to fix it now ;).
Thanks for Reading until next time.
Subscribe to my newsletter
Read articles from Parmeet Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Parmeet Singh
Parmeet Singh
Hi, I am Parmeet, a Software Engineer from India. 💡 Passionate about React, JS and algorithms that dance. When I’m not debugging, you can find me jamming out to playlists or swimming. Open doors to connect and let’s create something awesome together. 🌟