DEV Community

Cover image for Mastering useEffect: Handling Side Effects in React
Shefali
Shefali

Posted on • Edited on • Originally published at shefali.dev

Mastering useEffect: Handling Side Effects in React

If you want to use API calls, DOM updates, or timers in React, then mastering the useEffect hook is a must.

In this post, you’ll learn about the useEffect hook to handle side effects efficiently.

Before we get started, don’t forget to subscribe to my newsletter!
Get the latest tips, tools, and resources to level up your web development skills delivered straight to your inbox. Subscribe here!

Now let’s jump right into it!🚀

What is useEffect?

useEffect is a React Hook used to handle side effects in functional components.

Side effects are operations that occur after a component renders, such as:

  • API calls (fetching data)
  • DOM Manipulation (Title change, Event listeners)
  • Saving data in local storage
  • Timers (setTimeout, setInterval)

Before Hooks, class components used componentDidMount, componentDidUpdate, and componentWillUnmount for handling side effects. useEffect replaces all three in functional components.


Why You Need useEffect?

React follows a pure rendering approach, which means the UI should be predictable and free of side effects.

If you directly place API calls or DOM updates inside the component’s rendering, then you might face issues like:

  • Unexpected behavior: React may re-run the function multiple times, causing inconsistent updates.
  • Performance issues: Heavy operations (like API calls) running on every render can slow down the app.
  • Memory leaks: Timers and event listeners may keep running even after a component is removed.

To avoid these issues, React provides the useEffect hook. It ensures that side effects run after rendering, keeping the UI updates smooth and efficient.


Basic Syntax of useEffect

Before using useEffect, you need to import it from React:

import { useEffect } from "react";
Enter fullscreen mode Exit fullscreen mode

Syntax:

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

Here,

  • First argument: A function that executes the side effect.
  • Second argument (dependency array): Defines when the effect should run.

Simple Example of useEffect (Title Update)

import { useState, useEffect } from "react";

function TitleUpdater() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  });

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In this example, whenever the count updates, the title of the document will change.


Controlling When useEffect Runs

Empty Dependency Array ([ ])

useEffect(() => {
  console.log("Component Mounted!");
}, []);
Enter fullscreen mode Exit fullscreen mode

This will only run when the component is rendered for the first time. (like componentDidMount in class components).

Specific Dependency

If you want to run useEffect only when specific values change, then it’s necessary to provide the dependency array.

useEffect(() => {
  console.log(`Count changed to ${count}`);
}, [count]);
Enter fullscreen mode Exit fullscreen mode

This will run only when the count gets changed.

No Dependency

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

This will run after each render (not recommended unless necessary).


Fetching Data with useEffect

useEffect is commonly used for fetching data from an API.

import { useState, useEffect } from "react";

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

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then(response => response.json())
      .then(data => setUsers(data));
  }, []); // Empty dependency array: Runs only once when the component mounts

  return (
    <div>
      <h1>User List</h1>
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

API will be called only once when the component mounts. This is an efficient approach.


Cleanup Function (Avoiding Memory Leak)

If your side effects involve event listeners or timers, always clean up to prevent memory leaks.

Cleanup in setTimeout / setInterval

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

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

The interval will be cleared when the component unmounts.

Cleanup in Event Listeners

useEffect(() => {
  const handleResize = () => console.log("Window Resized!");

  window.addEventListener("resize", handleResize);

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

On Component unmount, the resize event listener will be removed, and this will prevent unnecessary executions.


Common Mistakes & Best Practices

Mistake 1

Mistake: Not providing a dependency array

useEffect(() => {
  fetch("https://jsonplaceholder.typicode.com/users")
    .then(response => response.json())
    .then(data => console.log(data));
});  // No dependency array → Infinite API calls
Enter fullscreen mode Exit fullscreen mode

Solution: Provide an empty dependency array ([])

useEffect(() => {
  fetch("https://jsonplaceholder.typicode.com/users")
    .then(response => response.json())
    .then(data => console.log(data));
}, []);  // API will be called only once
Enter fullscreen mode Exit fullscreen mode

Mistake 2

Mistake: Not using a cleanup function

useEffect(() => {
  setInterval(() => {
    console.log("Running...");
  }, 1000);
}, []);  // This will cause memory leak, if you don't provide clean up function
Enter fullscreen mode Exit fullscreen mode

Solution: Use the cleanup function

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

  return () => clearInterval(timer); // Cleanup function!
}, []);
Enter fullscreen mode Exit fullscreen mode

When to Use useEffect?

  • When you need to handle browser events (resize, scroll, etc.).
  • When you need to call APIs or fetch data.
  • When you need to update the DOM (title change, event listeners).
  • When you need to manage local storage or authentication.

🎯Wrapping Up

That’s all for today!

For paid collaboration connect with me at : [email protected]

I hope this post helps you.

If you found this post helpful, here’s how you can support my work:
Buy me a coffee – Every little contribution keeps me motivated!
📩 Subscribe to my newsletter – Get the latest tech tips, tools & resources.
𝕏 Follow me on X (Twitter) – I share daily web development tips & insights.

Keep coding & happy learning!

Top comments (2)

Collapse
 
nevodavid profile image
Nevo David

yeah this is def useful - using useEffect always messes with my brain at first

Collapse
 
devshefali profile image
Shefali

Thank you so much for checking out!