Popular Searches
Popular Course Categories
Popular Courses

Cleanup Functions

Cleanup Functions

React Side Effects and Lifecycle

Cleanup Functions in React JS

Cleanup Functions are an important concept in React JS that helps developers stop, remove, disconnect, cancel, or undo work started by the useEffect() Hook. They help manage resources such as timers, event listeners, API requests, subscriptions, and WebSocket connections when those resources are no longer needed.

Cleanup functions improve application reliability by preventing duplicate listeners, unnecessary background operations, outdated asynchronous updates, and resource leaks.

Course Link: JustAcademy React JS Training Course

Demo Link: Register for React JS Course Demo


1. What Is a Cleanup Function?

A cleanup function is an optional function returned by the setup function inside useEffect(). React executes it when the previous Effect needs to be stopped before a new setup, or when the component is removed from the React tree.

For example, if an Effect starts a timer, the cleanup function can clear the timer. If an Effect subscribes to a service, cleanup can unsubscribe from it.

Syntax

import { useEffect } from 'react';
function Example() {
  useEffect(() => {
    // Setup logic
    console.log("Effect started");
    return () => {
      // Cleanup logic
      console.log("Effect cleaned up");
    };
  }, []);
  return <h1>Cleanup Function Example</h1>;
}
export default Example;

Explanation:

  • useEffect() declares an Effect.
  • The setup function performs the required operation.
  • The returned cleanup function stops or reverses that operation.
  • The dependency array determines when the Effect must synchronize again.

2. Why Are Cleanup Functions Important?

Cleanup functions are important because some operations continue running independently of React's rendering process. If those operations are not stopped when necessary, they may cause unexpected behavior.

  • Prevent resource leaks: Release resources that are no longer required.
  • Clear timers: Stop intervals and scheduled timeouts.
  • Remove event listeners: Prevent old event handlers from continuing to run.
  • Unsubscribe from services: Stop receiving unnecessary external updates.
  • Close connections: Disconnect WebSockets and other external connections.
  • Handle asynchronous work: Abort supported requests or ignore outdated responses.
  • Avoid duplicate operations: Ensure that repeated Effect setup does not leave multiple resources active.

3. When Does a Cleanup Function Execute?

3.1 Before an Effect Runs Again

When one or more dependencies change, React runs the previous cleanup function before running the new Effect setup function.

3.2 When a Component Unmounts

When a component is removed from the React tree, React runs the cleanup functions for its active Effects.

3.3 During Strict Mode Development Checks

When React Strict Mode is enabled, React performs an additional setup-and-cleanup cycle for Effects during initial mounting in development. This helps detect missing or incorrect cleanup logic.

Effect Execution Flow

Component renders
       |
       v
React commits the UI
       |
       v
Effect setup executes
       |
       v
Dependencies change?
       |
       +---- Yes ----> Previous cleanup executes
       |                         |
       |                         v
       |                  New setup executes
       |
       +---- No -----> Effect remains active
 
Component unmounts
       |
       v
Final cleanup executes

Important: React does not require every Effect to return a cleanup function. Cleanup is needed when the Effect starts work that must be stopped or undone.


4. Cleanup Function with setInterval()

The setInterval() function executes a callback repeatedly after a specified interval. Use clearInterval() to stop it when it is no longer needed.

Example: Timer

import { useEffect, useState } from 'react';
function Timer() {
  const [seconds, setSeconds] = useState(0);
  useEffect(() => {
    const intervalId = setInterval(() => {
      setSeconds(previous => previous + 1);
    }, 1000);
    return () => {
      clearInterval(intervalId);
    };
  }, []);
  return <h2>Seconds: {seconds}</h2>;
}
export default Timer;

Explanation:

  1. The component renders and React commits the UI.
  2. The Effect starts an interval that increments the counter every second.
  3. The cleanup function calls clearInterval(intervalId).
  4. When the component unmounts, the interval is cleared.

The functional state updater previous => previous + 1 uses the latest state without requiring seconds in the dependency array. This avoids recreating the interval whenever the count changes.


5. Cleanup Function with setTimeout()

The setTimeout() function schedules a callback to execute after a specified delay. The scheduled callback can be cancelled using clearTimeout().

Example: Automatically Hide a Message

import { useEffect, useState } from 'react';
function Message() {
  const [visible, setVisible] = useState(true);
  useEffect(() => {
    const timeoutId = setTimeout(() => {
      setVisible(false);
    }, 5000);
    return () => {
      clearTimeout(timeoutId);
    };
  }, []);
  return (
    <div>
      {visible && <p>This message disappears after five seconds.</p>}
    </div>
  );
}
export default Message;

Explanation: The Effect schedules a timeout. If the component unmounts before the timeout fires, cleanup cancels it. Clearing a timeout after its callback has already executed does not undo that callback.


6. Cleanup Function with Event Listeners

Browser event listeners may continue responding to events even when a component no longer needs them. Cleanup removes listeners that were registered by an Effect.

Example: Window Resize Event

import { useEffect, useState } from 'react';
function WindowSize() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    function handleResize() {
      setWidth(window.innerWidth);
    }
    window.addEventListener("resize", handleResize);
    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);
  return <h2>Window Width: {width}px</h2>;
}
export default WindowSize;

Explanation:

  • The Effect registers a resize listener.
  • When the browser window is resized, the handler updates the width.
  • The cleanup function removes the same event handler.

Note: This example assumes a browser environment. In server-rendered applications, access to window should be handled safely on the client.


7. Cleanup Function with API Requests

An API request may finish after a component has updated or unmounted. Cleanup can abort supported requests or prevent outdated responses from updating the UI.

Example: Abort an API Request

import { useEffect, useState } from 'react';
function UserDetails({ userId }) {
  const [user, setUser] = useState(null);
  const [error, setError] = useState("");
  useEffect(() => {
    const controller = new AbortController();
    async function loadUser() {
      try {
        setUser(null);
        setError("");
        const response = await fetch(
          `https://jsonplaceholder.typicode.com/users/${userId}`,
          { signal: controller.signal }
        );
        if (!response.ok) {
          throw new Error("Failed to load user");
        }
        const data = await response.json();
        setUser(data);
      } catch (err) {
        if (err.name !== "AbortError") {
          setError(err.message);
        }
      }
    }
    loadUser();
    return () => {
      controller.abort();
    };
  }, [userId]);
  if (error) return <p>Error: {error}</p>;
  if (!user) return <p>Loading user...</p>;
  return <h2>{user.name}</h2>;
}
export default UserDetails;

Explanation:

  1. The Effect starts a request for the current userId.
  2. If userId changes or the component unmounts, cleanup calls controller.abort().
  3. The aborted request is handled separately from other errors.
  4. The next Effect setup fetches the new user's details when required.

This example uses the Fetch API, which supports AbortController. For asynchronous operations that cannot be cancelled, a cleanup flag can be used to ignore stale results.


8. Cleanup Function with Subscriptions

Subscriptions allow a component to receive updates from an external service. Cleanup should unsubscribe when the component no longer needs those updates.

Example

import { useEffect } from 'react';
function Notifications({ notificationService }) {
  useEffect(() => {
    function handleNotification(notification) {
      console.log("New notification:", notification);
    }
    notificationService.subscribe(handleNotification);
    return () => {
      notificationService.unsubscribe(handleNotification);
    };
  }, [notificationService]);
  return <p>Listening for notifications...</p>;
}
export default Notifications;

Explanation: The Effect subscribes to the notification service. If the component unmounts or the service dependency changes, cleanup removes the same handler from the subscription.

Note: This example assumes the service provides matching subscribe() and unsubscribe() methods.


9. Cleanup Function with WebSocket Connections

A WebSocket maintains a persistent connection between a client and a server. Cleanup should remove registered event listeners and close the connection when it is no longer required.

Example

import { useEffect, useState } from 'react';
function LiveUpdates({ url }) {
  const [message, setMessage] = useState("");
  useEffect(() => {
    const socket = new WebSocket(url);
    function handleMessage(event) {
      setMessage(event.data);
    }
    socket.addEventListener("message", handleMessage);
    return () => {
      socket.removeEventListener("message", handleMessage);
      socket.close();
    };
  }, [url]);
  return <p>Latest message: {message}</p>;
}
export default LiveUpdates;

Explanation: The Effect opens a connection and listens for messages. If the URL changes or the component unmounts, cleanup removes the listener and closes the connection.


10. Cleanup Functions and Dependency Changes

Cleanup does not run only when a component unmounts. It also runs before an Effect is set up again because a dependency has changed.

Example: Switching Chat Rooms

import { useEffect } from 'react';
function ChatRoom({ roomId }) {
  useEffect(() => {
    console.log("Connecting to:", roomId);
    return () => {
      console.log("Disconnecting from:", roomId);
    };
  }, [roomId]);
  return <h2>Room: {roomId}</h2>;
}
export default ChatRoom;

If roomId changes from "general" to "music", the normal sequence is:

  1. React commits the UI update.
  2. The previous cleanup runs using the old roomId, which is "general".
  3. The new Effect setup runs using the new roomId, which is "music".

When the component unmounts, React runs the cleanup for the active Effect.


11. Cleanup Functions in React Strict Mode

React Strict Mode is a development tool that helps identify bugs in components and Effects. When enabled, React performs an extra setup-and-cleanup cycle for Effects during initial mounting in development.

Example

useEffect(() => {
  console.log("Setup");
  return () => {
    console.log("Cleanup");
  };
}, []);

You may see the following sequence in development with Strict Mode enabled:

Setup
Cleanup
Setup

This is intentional. It checks whether cleanup correctly reverses the setup. Do not try to hide the extra execution with a ref; make sure every setup has the appropriate cleanup instead.


12. Common Cleanup Function Mistakes

Mistake Problem Correct Approach
Not clearing an interval The timer may continue running when it is no longer needed. Call clearInterval() in cleanup.
Not removing event listeners Old handlers may continue responding to events. Remove the same handler using removeEventListener().
Not unsubscribing The component may keep receiving external updates. Call the subscription API's matching unsubscribe method.
Not handling outdated requests An older response may overwrite newer data. Abort supported requests or ignore stale results.
Cleanup does not match setup Resources may remain active or be released incorrectly. Make cleanup reverse the work started by setup.
Assuming cleanup runs only on unmount Dependency changes can also trigger cleanup. Account for cleanup before Effect re-execution.
Trying to prevent Strict Mode checks Missing cleanup problems may remain hidden. Make setup and cleanup safe to repeat.

13. Best Practices for Cleanup Functions

  • Return a cleanup function when an Effect starts work that must be stopped or undone.
  • Keep setup and cleanup logic together inside the same Effect.
  • Use clearTimeout() and clearInterval() for scheduled operations.
  • Remove event listeners using the same event type and handler that were registered.
  • Unsubscribe from external services when subscriptions are no longer needed.
  • Abort supported requests or ignore outdated asynchronous results.
  • Include all reactive dependencies used by the Effect.
  • Do not add cleanup code for resources that do not require manual cleanup.
  • Test Effects with Strict Mode enabled during development.
  • Avoid using Effects for calculations that can be performed directly during rendering.

14. Difference Between Setup and Cleanup Functions

Feature Setup Function Cleanup Function
Purpose Starts or establishes work. Stops, removes, or reverses that work.
Execution Runs when the Effect is set up. Runs before necessary re-setup and on unmount.
Timer example Creates an interval. Clears the interval.
Event example Adds an event listener. Removes the event listener.
Connection example Connects to a server. Disconnects from the server.
Return value May return a cleanup function. Usually does not return another cleanup function.

15. Interview Questions and Answers

Q1. What is a cleanup function in React?

Answer: It is an optional function returned by a useEffect() setup function to stop, remove, or undo work started by the Effect.

Q2. When does a cleanup function execute?

Answer: It runs before an Effect re-executes with changed dependencies and when the component unmounts. Strict Mode can also trigger an extra cleanup cycle in development.

Q3. Why are cleanup functions important?

Answer: They help prevent unnecessary timers, listeners, subscriptions, and connections from remaining active.

Q4. How do you clean up a setInterval timer?

Answer: Store the interval ID and call clearInterval(intervalId) in the cleanup function.

Q5. How do you remove an event listener?

Answer: Call removeEventListener() with the same event type and handler used to register the listener.

Q6. Is a cleanup function always required?

Answer: No. It is required only when the Effect starts work that must be stopped or undone.

Q7. Does cleanup run before the next Effect setup?

Answer: Yes. When dependencies change, React runs the previous cleanup before the new setup.

Q8. Why might cleanup run during initial mounting in development?

Answer: React Strict Mode performs an extra setup-and-cleanup cycle to check whether the Effect is implemented correctly.

Q9. How can cleanup help with API requests?

Answer: It can abort supported requests using AbortController or prevent outdated responses from updating the UI.

Q10. What is the main rule for cleanup logic?

Answer: Cleanup should mirror setup and stop or reverse the work that setup started.


16. Practice Exercises

  1. Create a timer that increments a counter every second and clears the interval on unmount.
  2. Add a window resize listener and remove it in cleanup.
  3. Create a component that fetches user data and aborts the request when the user ID changes.
  4. Build a subscription example with matching subscribe and unsubscribe methods.
  5. Create a WebSocket example that closes the connection when the component unmounts.
  6. Enable React Strict Mode and verify that cleanup correctly reverses the setup.

17. Quick Revision Checklist

  • Understand the purpose of cleanup functions.
  • Know how to return cleanup from useEffect().
  • Understand cleanup on dependency changes and component unmounting.
  • Practice clearing timers and removing event listeners.
  • Know how to clean up subscriptions and external connections.
  • Understand request cancellation and stale-response handling.
  • Recognize the extra development setup-and-cleanup cycle in React Strict Mode.

18. Conclusion

Cleanup functions are essential for managing resources started by React Effects. They help stop timers, remove event listeners, unsubscribe from services, close connections, and handle asynchronous work when it is no longer relevant.

The key principle is simple: when an Effect starts work that must be stopped or undone, provide the appropriate cleanup. Correct cleanup makes React applications more predictable, maintainable, and reliable.

Official React Documentation: useEffect Reference | Synchronizing with Effects | Lifecycle of Reactive Effects

Learn React JS with JustAcademy: Explore React JS Training | Register for a Course Demo

whatsapp