DEV Community

Cover image for React Mastery Series – Day 13: React Hooks Deep Dive – Understanding useEffect
Siva Samanthapudi
Siva Samanthapudi

Posted on

React Mastery Series – Day 13: React Hooks Deep Dive – Understanding useEffect

Welcome back to the React Mastery Series!

In the previous article, we explored Forms in React and learned how controlled components help React manage user input.

Today, we will dive into one of the most important and widely used React Hooks:

useEffect Hook

useEffect is a fundamental Hook used in almost every real-world React application.

It helps us perform side effects such as:

  • Fetching data from APIs
  • Updating browser titles
  • Subscribing to events
  • Setting timers
  • Connecting to WebSockets
  • Cleaning up resources

Understanding useEffect is essential for building production-ready React applications.


What Are Side Effects?

Before understanding useEffect, we need to understand what a side effect is.

A side effect is any operation that happens outside the normal component rendering process.

Examples:

API Calls

Component Render
        |
        ↓
Call Backend API
        |
        ↓
Receive Data
        |
        ↓
Update State
Enter fullscreen mode Exit fullscreen mode

Browser Interaction

Examples:

  • Changing document title
  • Accessing local storage
  • Manipulating browser APIs

Subscriptions

Examples:

  • WebSocket connections
  • Event listeners
  • Timers

Why Do We Need useEffect?

React components should ideally be pure functions.

Example:

function UserProfile() {
  return <h1>User Profile</h1>;
}
Enter fullscreen mode Exit fullscreen mode

The component receives data and returns UI.

However, real applications need external interactions.

Example:

Render Component
       |
       ↓
Fetch User Data
       |
       ↓
Update UI
Enter fullscreen mode Exit fullscreen mode

React provides useEffect to handle these operations safely.


Basic Syntax of useEffect

useEffect(() => {
  // Side effect logic
}, []);
Enter fullscreen mode Exit fullscreen mode

The Hook accepts two parameters:

useEffect(
    function,
    dependency array
)
Enter fullscreen mode Exit fullscreen mode

First Parameter: Effect Function

This contains the code that should execute.

Example:

useEffect(() => {
  console.log("Component rendered");
});
Enter fullscreen mode Exit fullscreen mode

Second Parameter: Dependency Array

The dependency array controls when the effect runs.

Example:

useEffect(() => {
  console.log("Runs once");
}, []);
Enter fullscreen mode Exit fullscreen mode

The empty array means:

Run this effect only after the initial render.


Understanding useEffect Execution

Consider:

function App() {
  useEffect(() => {
    console.log("Effect executed");
  }, []);

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

Enter fullscreen mode Exit fullscreen mode

Execution flow:

Component Created
        |
        ↓
Initial Render
        |
        ↓
DOM Updated
        |
        ↓
useEffect Executes
Enter fullscreen mode Exit fullscreen mode

Important:

useEffect runs after the component renders.


useEffect Without Dependency Array

Example:

useEffect(()=>{
  console.log("Runs every render");
});
Enter fullscreen mode Exit fullscreen mode

Execution:

Initial Render
       ↓
Effect Runs

State Update
       ↓
Render
       ↓
Effect Runs Again
Enter fullscreen mode Exit fullscreen mode

This happens after every render.

Use carefully.


useEffect With Empty Dependency Array

Example:

useEffect(()=>{
  console.log("Runs once");
},[]);
Enter fullscreen mode Exit fullscreen mode

Execution:

Component Mount
        |
        ↓
Effect Executes Once
Enter fullscreen mode Exit fullscreen mode

Common use cases:

  • Initial API calls
  • Loading configuration
  • Setting up subscriptions

useEffect With Dependencies

Example:

useEffect(()=>{
  console.log("User changed");
},[userId]);
Enter fullscreen mode Exit fullscreen mode

Now the effect runs when:

userId changes
Enter fullscreen mode Exit fullscreen mode

Example:

setUserId(10);
Enter fullscreen mode Exit fullscreen mode

Flow:

State Update
      |
      ↓
Component Re-render
      |
      ↓
Dependency Changed
      |
      ↓
useEffect Executes
Enter fullscreen mode Exit fullscreen mode

Real-World Example: Fetching API Data

A common React pattern:

import { useEffect, useState } from "react";

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

  useEffect(() => {
    fetch("https://api.example.com/users")
      .then((response) => response.json())
      .then((data) => {
        setUsers(data);
      });
  }, []);

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

Enter fullscreen mode Exit fullscreen mode

Flow:

Component Loads
        |
        ↓
useEffect Runs
        |
        ↓
API Request
        |
        ↓
Receive Response
        |
        ↓
Update State
        |
        ↓
Render User List
Enter fullscreen mode Exit fullscreen mode

Cleanup Function in useEffect

Some effects need cleanup.

Examples:

  • Removing event listeners
  • Clearing timers
  • Closing WebSocket connections

Example:

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

    return () => {
        clearInterval(timer);
    };
}, []);
Enter fullscreen mode Exit fullscreen mode

The function returned from useEffect is called Cleanup Function


Component Lifecycle Using useEffect

In class components:

componentDidMount()

componentDidUpdate()

componentWillUnmount()
Enter fullscreen mode Exit fullscreen mode

In functional components:

useEffect handles all lifecycle behavior.


Mounting

Equivalent to:

componentDidMount()
Enter fullscreen mode Exit fullscreen mode

Example:

useEffect(()=>{
   loadData();
},[]);
Enter fullscreen mode Exit fullscreen mode

Runs once when component appears.


Updating

Equivalent to:

componentDidUpdate()
Enter fullscreen mode Exit fullscreen mode

Example:

useEffect(()=>{
   searchProducts();
},[searchText]);
Enter fullscreen mode Exit fullscreen mode

Runs whenever searchText changes.


Unmounting

Equivalent to:

componentWillUnmount()
Enter fullscreen mode Exit fullscreen mode

Example:

useEffect(() => {
    return () => {
        disconnect();
    };
}, []);

Enter fullscreen mode Exit fullscreen mode

Runs when component is removed.


Real-World Example: WebSocket Connection

Imagine a live banking notification system.

Component loads:

Open Dashboard
       |
       ↓
Create WebSocket Connection
       |
       ↓
Receive Transaction Alerts
Enter fullscreen mode Exit fullscreen mode

When user leaves:

Navigate Away
       |
       ↓
Close WebSocket
Enter fullscreen mode Exit fullscreen mode

Implementation:

useEffect(() => {
    const socket =
        new WebSocket(
            "wss://bank.com/alerts"
        );
    return () => {
        socket.close();
    };
}, []);

Enter fullscreen mode Exit fullscreen mode

Dependency Array Mistakes

Mistake 1: Missing Dependencies

Example:

useEffect(()=>{
   fetchUser(userId);
},[]);
Enter fullscreen mode Exit fullscreen mode

Problem:

userId is used but not included.

Correct:

useEffect(()=>{
   fetchUser(userId);
},[userId]);
Enter fullscreen mode Exit fullscreen mode

Mistake 2: Infinite Loops

Example:

useEffect(()=>{
  setCount(count+1);
},[count]);
Enter fullscreen mode Exit fullscreen mode

Flow:

count changes
      |
      ↓
Effect runs
      |
      ↓
setCount()
      |
      ↓
count changes again
      |
      ↓
Infinite Loop
Enter fullscreen mode Exit fullscreen mode

useEffect vs Event Handlers

A common confusion:

Should something happen inside:

  • useEffect?
  • Button click handler?

Example:

User clicks save button:

<button onClick={saveData}> Save </button>
Enter fullscreen mode Exit fullscreen mode

This belongs in an event handler.


Automatic behavior:

Component Loaded
       |
       ↓
Fetch Data
Enter fullscreen mode Exit fullscreen mode

This belongs in:

useEffect()
Enter fullscreen mode Exit fullscreen mode

useEffect Best Practices

Keep Effects Focused

Avoid:

useEffect(()=>{
  fetchData();
  updateTitle();
  connectSocket();
},[]);
Enter fullscreen mode Exit fullscreen mode

Better:

useEffect(()=>{
  fetchData();
},[]);


useEffect(()=>{
  connectSocket();
},[]);
Enter fullscreen mode Exit fullscreen mode

Avoid Unnecessary Effects

Do not use effects for simple calculations.

Avoid:

useEffect(()=>{
  setFullName( firstName + lastName);
},[firstName,lastName]);
Enter fullscreen mode Exit fullscreen mode

Instead:

const fullName = firstName + lastName;
Enter fullscreen mode Exit fullscreen mode

Always Cleanup Resources

Cleanup:

  • Timers
  • Subscriptions
  • Event listeners
  • Connections

Enterprise Example: Banking Dashboard

Consider a retail banking application.

When the dashboard opens:

User Opens Dashboard
          |
          ↓
useEffect Executes
          |
          ↓
Fetch Account Summary API
          |
          ↓
Fetch Transactions API
          |
          ↓
Update State
          |
          ↓
Render Dashboard
Enter fullscreen mode Exit fullscreen mode

When leaving:

User Leaves Page
          |
          ↓
Cleanup Runs
          |
          ↓
Close Connections
Enter fullscreen mode Exit fullscreen mode

This pattern is used in production React applications.


Key Takeaways

Today, we learned:

useEffect handles side effects in React.
✅ Effects run after rendering.
✅ Dependency arrays control execution timing.
✅ Empty dependency arrays run effects once.
✅ Cleanup functions prevent memory leaks.
✅ API calls, subscriptions, and timers commonly use useEffect.


Coming Next 🚀

In Day 14, we will explore:

React Hooks Deep Dive – useRef and useMemo

We will learn:

  • What useRef is
  • Accessing DOM elements
  • Persisting values without re-rendering
  • useMemo for performance optimization
  • Expensive calculations
  • Real-world optimization examples

These Hooks are essential for writing high-performance React applications.

Happy Coding! 🚀

Top comments (0)