Introduction
You have probably heard of the two words in your React.js journey: cleanup and unmount.
They both go hand in hand but were confusing to me for a long time in my React journey.
If we look at what the new React documentation says, it's a bit clearer than their previous documentation:
After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, and then run your setup function with the new values. After your component is removed from the DOM, React will run your cleanup function.
I still feel like the importance of the cleanup function and how it's related to unmount isn't clearly explained.
Disclaimer: I assume you've worked with useEffect
already.
Unmount
Unmount is such a fancy word. If a component unmounts from the page, it simply means it gets removed from the page.
This can happen for two reasons:
A state changes, causing the component not to show anymore, e.g., conditional rendering.
You navigate to a new page.
Example
We have two pages, and on each one, we're rendering two different components.
So, when can "unmount" happen for component A?
If the state that's responsible for conditionally rendering component A becomes false, then the component will be removed from DOM. "Removed from DOM" is essentially unmount in a human language.
The component is gone:
Okay, that's one case unmount can happen, what's the other case?
Navigating to a new page is what most people think of when they think unmount. But as we just saw, this doesn't have to be the case.
Now, we're on the second page. It's obvious whatever was on the previous page gets unmounted:
Now that we understand the concept of unmount, let's look into the cleanup function.
Cleanup
The cleanup function in useEffect
runs:
After the component unmounts.
After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, then it will run the effect with new values.
Let's imagine we're setting up a subscription:
useEffect(() => {
// Define a function to handle new messages
const handleMessage = (newMessage) => {
setMessages((prevMessages) => [...prevMessages, newMessage]);
};
// Subscribe to the chat service
chatService.subscribe(handleMessage);
}, []); // runs only once on mount
I've excluded the cleanup function. We should have a cleanup function that will unsubscribe from the chat service.
So, what could go wrong here?
Let's take a look at what could go wrong. We don't have the cleanup function, which would run after the component re-renders or gets removed from the DOM.
Examining the situation
What if we navigate to a new page and then come back to the previous page?
Or what happens if the component itself re-renders?
The useEffect
will run again, setting up a new subscription to the server.
But wait!? What happened to the previous subscription?
Yes, you're right. It's still there, and we have no way of removing it.
This isn't a one-time thing. You can imagine a real-world application where you have dozens of customers who frequently navigate all around the app. Yeah, it can quickly get messy.
List of issues
Memory Leaks: The component can't be garbage collected. This problem is more common in single-page applications, where moving between pages doesn't completely reload the app.
Performance Degradation: Active subscriptions in unmounted components use resources and may cause performance problems. Navigating away and back without the cleanup function could create multiple instances of the same subscription, using more resources than needed.
Unexpected Behavior and Errors: Attempting to update the state of an unmounted component (because the subscription is still active) can lead to errors. React warns against updates on unmounted components, as they can lead to unpredictable behavior.
Data Inconsistency: Multiple active subscriptions can cause state updates with conflicting or stale data, making the UI display incorrect information.
Summary
Unmount is when the component gets removed from the DOM.
The cleanup function runs after the component unmounts or after it re-renders but before the effect is set up with the new values. It's important to make sure things are properly cleaned up and don't cause a mess in the app when no longer needed.