🔄 Demystifying useEffect in React.js – The Sidekick You Never Knew You Needed 🦸♂️

Ah, useEffect
. The one hook that either makes you feel like a React wizard 🧙♂️ or makes you want to throw your laptop out the window 💻🚀.
Let’s break it down, desi-style, meme-style, and code-style. Buckle up! 🎢
🧠 What is useEffect
?
In simple terms: useEffect
lets you run some code (a side-effect) after your component renders.
Imagine your React component is a movie 🎬. Rendering is the scene. useEffect
is the background music or after-credits scene — something that happens alongside the main act.
🤷♂️ Why do we need it?
React is declarative. But what if you want to do things like:
Fetch data from an API 🌐
Set up a subscription 🧷
Add event listeners (like a scroll or resize) 🖱️
Update the DOM manually (rare, but hey… it happens) 💪
That’s where useEffect
comes in.
⚙️ Basic Syntax
import { useEffect } from "react";
useEffect(() => {
// your side-effect logic
});
By default, it runs after every render (yes, every 😩). But don’t worry, we’ll fix that.
🧪 Example 1: Console.log on Every Render
import { useEffect, useState } from "react";
function LoggerComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("Component rendered! 🔁");
});
return (
<div>
<p>You clicked {count} times.</p>
<button onClick={() => setCount(count + 1)}>Click me!</button>
</div>
);
}
📌 Every time you click, useEffect
runs.
🧯 Example 2: Run Only Once (on Mount)
Just like componentDidMount
in class components.
useEffect(() => {
console.log("Component mounted! 🚀");
}, []); // empty dependency array
✅ It runs only once when the component loads.
🔁 Example 3: Run When a Value Changes
useEffect(() => {
console.log("Count changed:", count);
}, [count]); // runs only when 'count' changes
Think of this like saying:
“Hey React, only run this if
count
has changed, okay?"
🧹 Example 4: Cleanup on Unmount
Perfect for things like removing event listeners or cancelling timers.
useEffect(() => {
const interval = setInterval(() => {
console.log("Tick ⏱️");
}, 1000);
return () => {
clearInterval(interval);
console.log("Cleanup done 🧼");
};
}, []);
🧼 The return function is the cleanup — React calls it when the component unmounts or before re-running the effect.
🧠 Mind = Blown: Dependency Gotchas 😵
Let’s say you’re fetching data based on id
prop:
useEffect(() => {
fetch(`/api/user/${id}`).then(...);
}, [id]);
✅ Good: only fetches when id
changes.
🚨 Bad: if you forget [id]
, it'll run on every render.
🧙♂️ Golden Rules of useEffect
Always add dependencies you use inside the effect.
Use multiple
useEffect
s for unrelated logic.Avoid putting async functions directly in
useEffect
(wrap them inside).
useEffect(() => {
async function fetchData() {
const res = await fetch(...);
}
fetchData();
}, []);
🧠 Real-World Example: Fetching Data
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let isMounted = true;
async function fetchUser() {
const res = await fetch(`/api/user/${userId}`);
const data = await res.json();
if (isMounted) setUser(data);
}
fetchUser();
return () => {
isMounted = false; // prevent state updates if unmounted
};
}, [userId]);
if (!user) return <p>Loading...</p>;
return <div>Hello, {user.name} 👋</div>;
}
TL;DR
useEffect(() => {}, [])
= run once.useEffect(() => {}, [value])
= run whenvalue
changes.useEffect(() => { return () => {} })
= cleanup.Always handle async stuff carefully 🧠.
🧶 Wrapping Up
useEffect
is powerful. But with great power comes... yep, great responsibility 🕸️.
Use it wisely. Split your concerns. Don’t fear the dependency array — embrace it.
If you found this helpful (or at least mildly entertaining 😄), share it with your React buddies.
Subscribe to my newsletter
Read articles from Amandeep Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Amandeep Singh
Amandeep Singh
At GetReplies we are building a SaaS product to craft personalized emails, messages to target 10% reply rate. Zolo fosters impactful contributions as an SDE-III, where frontend engineering expertise drives scalable web solutions. Proficiencies in ReactJS, Next.js, and Redux Toolkit enable the creation of user-centric, high-performance applications. Success is achieved through seamless collaboration and continuous improvement.