Hooks
useEffect
Master the useEffect Hook, covering mounting, updating, and unmounting phases, dependency arrays, cleanup actions, and infinite loop prevention.
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:
- Add a refresh button that updates a dummy
countdependency, triggering the profile fetch effect again. - 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(() => { |
useEffect(() => { (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(() => { |
useEffect(() => { |
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:
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
useEffectHook 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. |