ReviseAlgo Logo

Hooks

useEffect

Master the useEffect Hook, covering mounting, updating, and unmounting phases, dependency arrays, cleanup actions, and infinite loop prevention.

Last Updated: July 15, 2026 13 min read

1. Learning Objectives

In this lesson, you will master React's primary side-effect management Hook. By the end of this topic, you will be able to:

  • Synchronize React components with external systems (APIs, web sockets, timers).
  • Configure dependency arrays to control when side effects run.
  • Write cleanup functions to prevent memory leaks and duplicate connections.
  • Debug and resolve infinite layout rendering loops.
  • Understand the order of execution between renders, paint, and effects.

2. Overview

The useEffect Hook lets you synchronize a component with an external system. This includes fetching data from a database, listening to global window events, setting up sub-scriptions, and managing timers. useEffect schedules code execution after the component renders and the browser paints the UI on the screen.

3. Why This Topic Matters

Effects bridge the gap between React's declarative state and external systems:

  • Memory Leak Prevention: Setting up event listeners (like scroll or mouse movement listeners) or timers (like setInterval) without cleaning them up causes memory leaks, slowing down the browser.
  • Data Synchronization: Fetching data when parameters (like route page IDs or query strings) change ensures the page displays the correct content.

4. Real-World Analogy

Think of useEffect like **a library book subscription**:

  • Borrowing the Book (The Effect): You register at the library and borrow a book (e.g. setting up a websocket connection).
  • Returning the Book (The Cleanup): When you are finished reading, or if you want to borrow a different book (dependencies update), you return the first book to clear your library record. If you leave the library without returning the book (unmounting without cleaning up), you get fined (a memory leak).

5. Core Concepts

Below is a comparison of dependency array configurations in useEffect:

Dependency Array Configuration When the Effect Runs Common Use Cases
useEffect(() => {}) After *every single* render cycle. Rarely used; generally a mistake. Can cause performance bottlenecks.
useEffect(() => {}, []) Only once, when the component mounts. Initializing global event listeners, loading initial dashboard data.
useEffect(() => {}, [userId]) On mount, and whenever userId changes. Fetching user profile details, resetting forms on user switch.

6. Syntax & API Reference

This example shows how side effects are declared, synchronized, and cleaned up:

7. Visual Diagram

This diagram displays the execution order of renders, paint events, effects, and cleanups:

8. Live Example — Full Working Code

Below is a complete, single-file HTML page showing loading indications and API fetching synchronization:

What just happened? Clicking "User 2" updates the userId state. Since userId is a dependency of the effect, React schedules the cleanup function, updates the DOM, and runs the effect again. The effect simulates an API request, updating the userProfile state once the mock request completes.

9. Interactive Playground

Try It Yourself Challenges:

  1. Add a refresh button that updates a dummy count dependency, triggering the profile fetch effect again.
  2. Modify the timeout duration or simulate a network error to test how the UI handles errors.

10. Common Mistakes

Mistake Why it happens Wrong Correct
Omitting the dependency array Omitting the dependency array entirely causes the effect to run on *every* render cycle, slowing down the application and potentially causing infinite fetch loops. useEffect(() => {
fetchData();
});
useEffect(() => {
fetchData();
}, []);
(includes empty array to run only once on mount)
Omitting cleanups for subscriptions or timers If you set up event listeners or timers inside an effect without returning a cleanup function, those operations continue to run in the background after the component unmounts, causing memory leaks. useEffect(() => {
setInterval(handler, 1000);
}, []);
useEffect(() => {
const id = setInterval(handler, 1000);
return () => clearInterval(id);
}, []);

11. Best Practices

  • Always clean up effects: Ensure any event listeners, websocket connections, or timers set up in an effect are cleared by returning a cleanup function.
  • Include all dependencies: Ensure all variables read inside the effect function are listed in the dependency array to prevent stale closures.
  • Separate concerns: Use multiple `useEffect` Hooks to split unrelated operations into separate blocks, rather than combining them into a single, complex effect.
  • Prevent race conditions: Use cleanup functions or flags to cancel state updates if a component unmounts or dependencies change before an asynchronous operation completes.

12. Browser Compatibility/Requirements

The useEffect Hook is supported in all modern web browsers.

13. Interview Questions

Q1: When does a React component execute the cleanup function returned by `useEffect`?

Answer: React executes the cleanup function in two scenarios: (1) immediately before the main effect code runs again (when dependencies change), clearing any operations set up during the previous render, and (2) when the component is unmounted from the DOM, cleaning up any active connections or timers.

Q2: How do you prevent infinite rendering loops when using `useEffect` to fetch data and update component state?

Answer: An infinite loop occurs when an effect updates a state variable, and that same state variable is listed in the effect's dependency array. To prevent this, ensure that the state setter is either called conditionally, or that the dependency array only contains external inputs (like props or route parameters) instead of local variables updated by the effect.

14. Debugging Exercise

Identify the bug in this component that causes it to trigger infinite network fetch requests:

View Solution

Diagnosis: The effect is missing a dependency array, meaning it runs on every render cycle. When the fetch request completes, it calls setProfile, which updates the state and triggers a re-render. This re-render runs the effect again, triggering another fetch request and causing an infinite loop.

Fix: Add `userId` to the dependency array so the effect only runs when the user ID changes:

15. Practice Exercises

Exercise 1: Create a digital clock widget

Build a component named DigitalClock that displays the current time, updating the seconds value in real-time. Use setInterval inside an effect, and clean up the interval when the component is unmounted.

16. Scenario-Based Challenge

The Real-Time Chat WebSocket Subscription:

You are building a chat application. When a user enters a chat room, the component opens a WebSocket connection to receive messages in real-time. Switching rooms should close the previous WebSocket connection and open a new one. Describe how you would implement this using the useEffect Hook and its cleanup function.

17. Quick Quiz

Q1: When does a React component execute the main body of a useEffect hook?

A) Synchronously during the layout rendering phase.

B) Asynchronously after the render is painted to the screen.

C) Instantly before the state setter function completes.

Answer: B — React runs effects asynchronously after the component renders and the browser paints the UI on the screen.

18. Summary & Key Takeaways

  • The useEffect Hook is used to synchronize components with external systems.
  • The dependency array controls when the effect code runs.
  • Return a cleanup function from the effect to clear event listeners, intervals, or subscriptions.
  • Ensure all variables read inside the effect are listed in the dependency array.

19. Cheat Sheet

Effect Signature Description
useEffect(fn) Runs after *every* single render.
useEffect(fn, []) Runs *only once* on mount.
useEffect(fn, [val]) Runs on mount, and whenever val changes.
return () => cleanup() Cleanup function clears subscriptions and timers.
---