React js useEffect Cleanup Practice Questions with Solutions

Introduction

The cleanup function in useEffect is used to stop or remove something that an effect started. It is useful when working with timers, event listeners, subscriptions, connections, or other external resources. Cleanup helps prevent unwanted behavior and unnecessary resource usage when dependencies change or a component is removed. In this chapter, we will solve practical questions to understand how and when to use useEffect cleanup. React js useEffect Cleanup practice questions with solutions help to understand the concepts.

1. What is Cleanup in useEffect?

Cleanup is a function returned from the useEffect callback.

Example:

import { useEffect } from "react";

function App() {
  useEffect(() => {
    console.log("Effect started");

    return () => {
      console.log("Effect cleaned up");
    };
  }, []);

  return <h1>Hello React</h1>;
}

export default App;

The function returned from the effect is called the cleanup function.

It is used to stop, remove, or disconnect resources created by the effect.


2. Why is Cleanup needed in useEffect?

Some effects create resources or external connections that should not continue running indefinitely.

For example:

  • setInterval()
  • setTimeout()
  • Browser event listeners
  • Subscriptions
  • WebSocket connections
  • External connections

Without proper cleanup, an old timer or listener may continue working even when it is no longer needed.

Cleanup allows React to remove or stop these resources at the appropriate time.


3. How do you create a Cleanup Function in useEffect?

Return a function from the effect.

useEffect(() => {
  console.log("Effect started");

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

The function after return is the cleanup function.

The basic structure is:

useEffect(() => {
  // Setup or effect logic

  return () => {
    // Cleanup logic
  };
}, []);

Cleanup should reverse or stop what the effect started whenever cleanup is required.


4. How do you clean up a setInterval() in React?

Suppose an effect starts an interval:

import { useEffect } from "react";

function Timer() {
  useEffect(() => {
    const intervalId = setInterval(() => {
      console.log("Timer running");
    }, 1000);

    return () => {
      clearInterval(intervalId);
    };
  }, []);

  return <h2>Timer</h2>;
}

export default Timer;

Here:

  1. setInterval() starts the timer.
  2. React stores the interval ID in intervalId.
  3. The cleanup function calls clearInterval().
  4. The interval is stopped when the effect is cleaned up.

This prevents the interval from continuing after it is no longer needed.


5. How do you clean up a setTimeout() in React?

You can use clearTimeout() inside the cleanup function.

import { useEffect } from "react";

function Message() {
  useEffect(() => {
    const timerId = setTimeout(() => {
      console.log("Message displayed");
    }, 3000);

    return () => {
      clearTimeout(timerId);
    };
  }, []);

  return <h2>Welcome</h2>;
}

export default Message;

The cleanup function cancels the timeout if the effect needs to be cleaned up before the timer finishes.


6. How do you remove an Event Listener using useEffect Cleanup?

When adding an event listener, remove the same listener during cleanup.

import { useEffect } from "react";

function WindowTracker() {
  useEffect(() => {
    const handleResize = () => {
      console.log("Window resized");
    };

    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);

  return <h2>Resize the browser window</h2>;
}

export default WindowTracker;

Here:

  • addEventListener() adds the listener.
  • removeEventListener() removes it.
  • The same function reference is used for both operations.

This prevents old event listeners from remaining active.


7. When does the useEffect Cleanup Function run?

Cleanup can run in important situations.

For an effect with dependencies, React runs the previous cleanup before running the next effect setup when the dependencies have changed.

Example:

useEffect(() => {
  console.log("Effect started");

  return () => {
    console.log("Previous effect cleaned up");
  };
}, [count]);

When count changes, React cleans up the previous effect before running the new setup.

Cleanup also runs when the component is removed from the screen.

In development with React Strict Mode, React may run an extra setup → cleanup → setup cycle to help detect effect bugs.


8. How do you use Cleanup with a Changing Dependency?

Suppose a component subscribes to something based on a userId.

import { useEffect } from "react";

function User({ userId }) {
  useEffect(() => {
    console.log("Subscribe to user:", userId);

    return () => {
      console.log("Unsubscribe from user:", userId);
    };
  }, [userId]);

  return <h2>User ID: {userId}</h2>;
}

export default User;

When userId changes:

  1. React cleans up the previous effect.
  2. The previous subscription can be removed.
  3. The new effect setup runs using the new userId.

This pattern is useful when an effect depends on changing props or state.


9. How do you clean up an API Request in useEffect?

For requests that support cancellation, AbortController can be used.

import { useEffect, useState } from "react";

function Users() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    const controller = new AbortController();

    async function fetchUsers() {
      try {
        const response = await fetch(
          "https://jsonplaceholder.typicode.com/users",
          {
            signal: controller.signal,
          }
        );

        const data = await response.json();
        setUsers(data);
      } catch (error) {
        if (error.name !== "AbortError") {
          console.error(error);
        }
      }
    }

    fetchUsers();

    return () => {
      controller.abort();
    };
  }, []);

  return (
    <div>
      <h2>Users</h2>

      {users.map((user) => (
        <p key={user.id}>{user.name}</p>
      ))}
    </div>
  );
}

export default Users;

Here, the cleanup function calls:

controller.abort();

This requests cancellation of the fetch when the effect is cleaned up.


10. Build a Practical Timer using useEffect Cleanup

Create a timer that increases every second and properly cleans up its interval.

Solution:

import { useEffect, useState } from "react";

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setSeconds((previousSeconds) => previousSeconds + 1);
    }, 1000);

    return () => {
      clearInterval(intervalId);
    };
  }, []);

  return (
    <div>
      <h1>Timer</h1>
      <h2>{seconds} seconds</h2>
    </div>
  );
}

export default Timer;

How it works:

  • useState() stores the number of seconds.
  • setInterval() runs every second.
  • The functional updater increases the previous value.
  • clearInterval() stops the timer during cleanup.
  • The empty dependency array means the effect has no changing reactive dependencies.

The cleanup is important because it ensures the interval does not continue after the component is removed.

Key Takeaways

  • A cleanup function is returned from useEffect.
  • Cleanup is used to stop or remove resources created by an effect.
  • clearInterval() can clean up intervals.
  • clearTimeout() can clean up timeouts.
  • Event listeners should be removed during cleanup.
  • Subscriptions and external connections should be disconnected during cleanup.
  • When dependencies change, React cleans up the previous effect before running the next setup.
  • Cleanup also runs when a component is removed from the screen.
  • AbortController can be used to cancel supported fetch requests.
  • React Strict Mode may perform an extra setup and cleanup cycle in development.
  • Not every useEffect needs a cleanup function. Cleanup is needed when the effect sets up something that must be stopped or removed.

FAQs

1. What is a cleanup function in React?

A cleanup function is a function returned from useEffect that is used to stop or remove resources created by the effect.

2. Why do we need cleanup in useEffect?

Cleanup helps prevent unwanted timers, event listeners, subscriptions, or external connections from continuing when they are no longer needed.

3. How do I clean up setInterval()?

Use clearInterval() inside the cleanup function.

useEffect(() => {
  const id = setInterval(() => {
    console.log("Running");
  }, 1000);

  return () => clearInterval(id);
}, []);

4. How do I remove an event listener in useEffect?

Use removeEventListener() inside the cleanup function with the same event type and function reference used when adding the listener.

5. When does the cleanup function run?

Cleanup runs before the effect is re-synchronized because dependencies changed and when the component is removed from the screen.

6. Can cleanup be used with API requests?

Yes. For APIs that support cancellation, AbortController can be used to cancel an in-progress fetch request during cleanup.

7. Does every useEffect need cleanup?

No. Cleanup is only needed when the effect creates something that needs to be stopped, disconnected, unsubscribed, removed, or cancelled.

Written by Shubhranshu Shekhar, who has trained 20000+ students in coding.

Scroll to Top