❓ Introduction: What is useEffect?
useEffectis a React Hook that lets you perform side effects in function components.A side effect is anything that:
Interacts with the outside world
Doesn’t directly return UI
Common side effects:
Fetching data from an API
Updating the browser title
Adding event listeners
Setting timers (
setTimeout,setInterval)Subscribing / unsubscribing to services
👉 Before Hooks, these were handled using lifecycle methods in class components.
🔴 Side Effects in Class Components
In class components, side effects are spread across lifecycle methods:
Lifecycle Method | Purpose |
|---|---|
| Runs after first render |
| Runs after updates |
| Cleanup before removal |
❌ Problems:
Repetition – same logic in multiple lifecycle methods
Fragmentation – setup in one place, cleanup in another
Hard to maintain – unrelated code grouped together
<div class="infographic-lifeeffect" id="lu">
<style>
.infographic-lifeeffect {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-deep: #1942a8;
--accent-soft: #e3ebfd;
--line: #ddd6c9;
--radius: 8px;
--display-font: 'Fraunces', Georgia, serif;
--body-font: 'Inter', system-ui, sans-serif;
--mono-font: 'IBM Plex Mono', monospace;
max-width: 940px;
margin: 0 auto;
box-sizing: border-box;
background: transparent;
color: var(--ink);
font-family: var(--body-font);
}
.infographic-lifeeffect[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #13171f;
--paper-alt: #1c212b;
--accent: #5b8def;
--accent-deep: #8fb4ff;
--accent-soft: #20293f;
--line: #2c313d;
}
.infographic-lifeeffect * { box-sizing: border-box; min-width: 0; }
.lu-eyebrow {
font-family: var(--mono-font); font-size: 0.72rem; letter-spacing: 0.1em;
text-transform: uppercase; color: var(--accent); margin: 0 0 0.45rem;
display: flex; align-items: center; justify-content: center; gap: 0.4rem;
}
.lu-eyebrow::before { content: ""; width: 14px; height: 2px; background: var(--accent); display: inline-block; }
.lu-title {
font-family: var(--display-font); font-size: clamp(1.4rem, 4vw, 2.15rem); text-align: center;
margin: 0 0 0.5rem; line-height: 1.18; letter-spacing: -0.01em;
}
.lu-sub { color: var(--ink-soft); font-size: 0.95rem; text-align: center; margin: 0 0 1.4rem; }
.lu-toggle-row { display: flex; justify-content: center; gap: 0.6rem; margin-bottom: 1.4rem; flex-wrap: wrap; }
.lu-toggle {
font: inherit; font-weight: 600; font-size: 0.85rem; color: var(--ink); background: var(--paper-alt);
border: 1px solid var(--line); border-radius: 100px; padding: 0.5rem 1.15rem; cursor: pointer;
transition: background 0.2s, border-color 0.2s, color 0.2s, transform 0.15s;
}
.lu-toggle:hover { transform: translateY(-1px); }
.lu-toggle.is-active { background: var(--accent); border-color: var(--accent); color: #fff; }
.lu-toggle:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.lu-caption {
text-align: center; font-size: 0.92rem; color: var(--ink); min-height: 2.8em; display: flex;
align-items: center; justify-content: center; max-width: 640px; margin: 0 auto 1.5rem; line-height: 1.5;
padding: 0.6rem 1.1rem; background: var(--accent-soft); border-radius: 10px;
}
.lu-caption b { color: var(--accent-deep); }
.lu-caption code { font-family: var(--mono-font); }
.lu-panels { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; margin-bottom: 1.4rem; }
@media (max-width: 720px) { .lu-panels { grid-template-columns: 1fr; } }
.lu-panel-label {
text-align: center; font-family: var(--mono-font); font-size: 0.72rem; letter-spacing: 0.06em;
text-transform: uppercase; color: var(--ink-soft); margin: 0 0 0.5rem;
}
.lu-code-block pre {
margin: 0; background: #0d0d10; border: 1px solid #24252d; border-radius: var(--radius);
padding: 0.9rem; overflow: auto; max-height: 460px;
}
.lu-code-block code { font-family: var(--mono-font); font-size: 0.68rem; line-height: 1.7; color: #d4d4d8; white-space: pre; }
.tok-kw { color: #c678dd; }
.tok-tag { color: #e06c75; }
.tok-type { color: #61afef; }
.tok-attr { color: #d19a66; }
.tok-str { color: #98c379; }
.tok-expr { color: #56b6c2; }
.tok-punc { color: #6b7280; }
.tok-comment { color: #7f848e; font-style: italic; }
.lu-hl { border-radius: 3px; transition: background 0.3s ease, box-shadow 0.3s ease; }
.lu-hl.is-on { background: rgba(37,99,235,0.28); box-shadow: 0 0 0 1px rgba(37,99,235,0.4); }
.lu-hl.is-cleanup.is-on { background: rgba(220,38,38,0.22); box-shadow: 0 0 0 1px rgba(220,38,38,0.4); }
.lu-takeaway {
padding: 0.95rem 1.2rem; background: var(--accent-soft); border-left: 3px solid var(--accent);
border-radius: 4px; font-size: 0.9rem; color: var(--ink); line-height: 1.5; max-width: 700px; margin: 0 auto;
}
.lu-takeaway b { color: var(--accent-deep); }
.lu-takeaway code { font-family: var(--mono-font); }
@media (prefers-reduced-motion: reduce) {
.infographic-lifeeffect *, .infographic-lifeeffect *::before, .infographic-lifeeffect *::after {
transition-duration: 0.001ms !important; animation-duration: 0.001ms !important;
}
}
</style>
<div class="lu-eyebrow">Class vs Hooks</div>
<h1 class="lu-title">Lifecycle Methods → useEffect</h1>
<p class="lu-sub">Click a phase to see which lines do the same job in both components.</p>
<div class="lu-toggle-row">
<button class="lu-toggle" data-phase="mount">🔹 Mount</button>
<button class="lu-toggle" data-phase="update">🔹 Update</button>
<button class="lu-toggle" data-phase="unmount">🔹 Unmount</button>
</div>
<div class="lu-panels">
<div>
<p class="lu-panel-label">Class Component</p>
<div class="lu-code-block"><pre><code id="luClassCode"></code></pre></div>
</div>
<div>
<p class="lu-panel-label">Function Component + Hooks</p>
<div class="lu-code-block"><pre><code id="luHookCode"></code></pre></div>
</div>
</div>
</div>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fraunces:wght@500;600&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<script>
(function () {
var root = document.getElementById('lu');
if (!root) return;
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') dark = true;
else if (bodyAttr === 'false') dark = false;
else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) root.setAttribute('data-theme', 'dark');
window.addEventListener('message', function (e) {
if (e.data && e.data.type === 'darkMode') {
if (e.data.value) root.setAttribute('data-theme', 'dark');
else root.removeAttribute('data-theme');
}
});
var classCode =
'<span class="tok-kw">import</span> <span class="tok-type">React</span><span class="tok-punc">,</span> <span class="tok-punc">{</span> Component <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">"react"</span><span class="tok-punc">;</span>\n\n' +
'<span class="tok-kw">class</span> <span class="tok-type">Counter</span> <span class="tok-kw">extends</span> <span class="tok-type">Component</span> <span class="tok-punc">{</span>\n' +
' <span class="tok-kw">constructor</span><span class="tok-punc">(</span>props<span class="tok-punc">) {</span>\n' +
' super<span class="tok-punc">(</span>props<span class="tok-punc">);</span>\n' +
' <span class="tok-kw">this</span>.state <span class="tok-punc">=</span> <span class="tok-punc">{</span> <span class="tok-attr">count</span><span class="tok-punc">:</span> <span class="tok-expr">0</span> <span class="tok-punc">};</span>\n' +
' <span class="tok-kw">this</span>.timer <span class="tok-punc">=</span> <span class="tok-kw">null</span><span class="tok-punc">;</span>\n' +
' <span class="tok-punc">}</span>\n\n' +
' <span id="luCmpMount" class="lu-hl">componentDidMount<span class="tok-punc">() {</span>\n' +
' document.title <span class="tok-punc">=</span> <span class="tok-str">`Clicked ${<span class="tok-kw">this</span>.state.count} times`</span><span class="tok-punc">;</span>\n' +
' <span class="tok-kw">this</span>.timer <span class="tok-punc">=</span> setInterval<span class="tok-punc">(() => {</span>\n' +
' console.log<span class="tok-punc">(</span><span class="tok-str">"Timer running..."</span><span class="tok-punc">);</span>\n' +
' <span class="tok-punc">},</span> <span class="tok-expr">2000</span><span class="tok-punc">);</span>\n' +
' <span class="tok-punc">}</span></span>\n\n' +
' <span id="luCmpUpdate" class="lu-hl">componentDidUpdate<span class="tok-punc">(</span>prevProps<span class="tok-punc">,</span> prevState<span class="tok-punc">) {</span>\n' +
' <span class="tok-kw">if</span> <span class="tok-punc">(</span>prevState.count <span class="tok-punc">!==</span> <span class="tok-kw">this</span>.state.count<span class="tok-punc">) {</span>\n' +
' document.title <span class="tok-punc">=</span> <span class="tok-str">`Clicked ${<span class="tok-kw">this</span>.state.count} times`</span><span class="tok-punc">;</span>\n' +
' <span class="tok-punc">}</span>\n' +
' <span class="tok-punc">}</span></span>\n\n' +
' <span id="luCmpUnmount" class="lu-hl">componentWillUnmount<span class="tok-punc">() {</span>\n' +
' clearInterval<span class="tok-punc">(</span><span class="tok-kw">this</span>.timer<span class="tok-punc">);</span>\n' +
' <span class="tok-punc">}</span></span>\n\n' +
' increment <span class="tok-punc">=</span> <span class="tok-punc">() => {</span>\n' +
' <span class="tok-kw">this</span>.setState<span class="tok-punc">({</span> <span class="tok-attr">count</span><span class="tok-punc">:</span> <span class="tok-kw">this</span>.state.count <span class="tok-punc">+</span> <span class="tok-expr">1</span> <span class="tok-punc">});</span>\n' +
' <span class="tok-punc">};</span>\n\n' +
' render<span class="tok-punc">() {</span>\n' +
' <span class="tok-kw">return</span> <span class="tok-punc">(</span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">div</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">h2</span><span class="tok-punc">></span>Count<span class="tok-punc">:</span> <span class="tok-punc">{</span><span class="tok-kw">this</span>.state.count<span class="tok-punc">}</</span><span class="tok-tag">h2</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">button</span> <span class="tok-attr">onClick</span><span class="tok-punc">={</span><span class="tok-kw">this</span>.increment<span class="tok-punc">}></span>Increment<span class="tok-punc"></</span><span class="tok-tag">button</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc"></</span><span class="tok-tag">div</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc">);</span>\n' +
' <span class="tok-punc">}</span>\n' +
'<span class="tok-punc">}</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">default</span> <span class="tok-type">Counter</span><span class="tok-punc">;</span>';
var hookCode =
'<span class="tok-kw">import</span> <span class="tok-type">React</span><span class="tok-punc">,</span> <span class="tok-punc">{</span> useState<span class="tok-punc">,</span> useEffect <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">"react"</span><span class="tok-punc">;</span>\n\n' +
'<span class="tok-kw">function</span> <span class="tok-type">Counter</span><span class="tok-punc">() {</span>\n' +
' <span class="tok-kw">const</span> <span class="tok-punc">[</span>count<span class="tok-punc">,</span> setCount<span class="tok-punc">]</span> <span class="tok-punc">=</span> useState<span class="tok-punc">(</span><span class="tok-expr">0</span><span class="tok-punc">);</span>\n\n' +
' useEffect<span class="tok-punc">(() => {</span>\n' +
' <span id="luHookBody" class="lu-hl">document.title <span class="tok-punc">=</span> <span class="tok-str">`Clicked ${count} times`</span><span class="tok-punc">;</span>\n\n' +
' <span class="tok-kw">const</span> timer <span class="tok-punc">=</span> setInterval<span class="tok-punc">(() => {</span>\n' +
' console.log<span class="tok-punc">(</span><span class="tok-str">"Timer running..."</span><span class="tok-punc">);</span>\n' +
' <span class="tok-punc">},</span> <span class="tok-expr">2000</span><span class="tok-punc">);</span></span>\n\n' +
' <span id="luHookCleanup" class="lu-hl lu-cleanup"><span class="tok-kw">return</span> <span class="tok-punc">() => {</span>\n' +
' clearInterval<span class="tok-punc">(</span>timer<span class="tok-punc">);</span>\n' +
' console.log<span class="tok-punc">(</span><span class="tok-str">"Timer cleared"</span><span class="tok-punc">);</span>\n' +
' <span class="tok-punc">};</span></span>\n' +
' <span class="tok-punc">},</span> <span id="luHookDeps" class="lu-hl tok-punc">[count]</span><span class="tok-punc">);</span> <span class="tok-comment">// runs when count changes</span>\n\n' +
' <span class="tok-kw">return</span> <span class="tok-punc">(</span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">div</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">h2</span><span class="tok-punc">></span>Count<span class="tok-punc">:</span> <span class="tok-punc">{</span>count<span class="tok-punc">}</</span><span class="tok-tag">h2</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">button</span> <span class="tok-attr">onClick</span><span class="tok-punc">={</span><span class="tok-punc">() =></span> setCount<span class="tok-punc">(</span>count <span class="tok-punc">+</span> <span class="tok-expr">1</span><span class="tok-punc">)}></span>\n' +
' Increment\n' +
' <span class="tok-punc"></</span><span class="tok-tag">button</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc"></</span><span class="tok-tag">div</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc">);</span>\n' +
'<span class="tok-punc">}</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">default</span> <span class="tok-type">Counter</span><span class="tok-punc">;</span>';
root.querySelector('#luClassCode').innerHTML = classCode;
root.querySelector('#luHookCode').innerHTML = hookCode;
var toggles = Array.prototype.slice.call(root.querySelectorAll('.lu-toggle'));
var allHl = Array.prototype.slice.call(root.querySelectorAll('.lu-hl'));
var phases = {
mount: {
left: ['luCmpMount'],
right: ['luHookBody']
},
update: {
left: ['luCmpUpdate'],
right: ['luHookCleanup', 'luHookBody', 'luHookDeps']
},
unmount: {
left: ['luCmpUnmount'],
right: ['luHookCleanup']
}
};
var active = null;
function scrollToId(id) {
var el = root.querySelector('#' + id);
if (!el) return;
el.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'smooth' });
}
function apply(phase) {
allHl.forEach(function (el) { el.classList.remove('is-on'); });
if (active === phase) {
active = null;
toggles.forEach(function (t) { t.classList.remove('is-active'); });
return;
}
active = phase;
var cfg = phases[phase];
cfg.left.concat(cfg.right).forEach(function (id) {
var el = root.querySelector('#' + id);
if (el) el.classList.add('is-on');
});
toggles.forEach(function (t) { t.classList.toggle('is-active', t.getAttribute('data-phase') === phase); });
// scroll each code panel to its highlighted region
scrollToId(cfg.left[0]);
scrollToId(cfg.right[0]);
}
toggles.forEach(function (t) {
t.addEventListener('click', function () { apply(t.getAttribute('data-phase')); });
});
})();
</script>❓ Why useEffect?
✔️ useEffect replaces all three lifecycle methods
✔️ Groups related logic in one place
✔️ Makes components cleaner & easier to understand
What is useEffect & When Does It Run?
useEffectruns after the component rendersBy default:
Runs after first render
Runs after every update
💡 React says:
“Don’t think in lifecycle phases — think in effects.”
✏️ Syntax of useEffect
useEffect(() => {
// Side effect logic
return () => {
// Cleanup logic (optional)
};
}, [dependencies]);Explanation:
Effect function → Runs after render
Cleanup function → Runs before unmount OR before re-running effect
Dependency array → Controls when effect runs
💊 Dependency Array – How It Works
Dependency Array | When effect runs |
|---|---|
❌ Not provided | After every render |
| Only once (on mount) |
| When |
Simple Example: Update Document Title
🎯 Goal:
Update browser tab title when count changes.
✅ Function Component with useEffect
<div class="infographic-ueout" id="uo">
<style>
.infographic-ueout {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--line: #ddd6c9;
--accent: #2563eb;
--radius: 8px;
--mono-font: 'IBM Plex Mono', monospace;
--body-font: 'Inter', system-ui, sans-serif;
max-width: 700px;
margin: 0 auto;
box-sizing: border-box;
background: transparent;
color: var(--ink);
font-family: var(--body-font);
}
.infographic-ueout[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--line: #2c313d;
--accent: #5b8def;
}
.infographic-ueout * { box-sizing: border-box; min-width: 0; }
.uo-grid { display: flex; flex-direction: column; gap: 1.1rem; align-items: stretch; }
.uo-code-block pre {
margin: 0; background: #0d0d10; border: 1px solid #24252d; border-radius: var(--radius);
padding: 0.9rem; overflow: auto; max-height: 360px;
}
.uo-code-block code { font-family: var(--mono-font); font-size: 0.78rem; line-height: 1.75; color: #d4d4d8; white-space: pre; }
.tok-kw { color: #c678dd; }
.tok-tag { color: #e06c75; }
.tok-type { color: #61afef; }
.tok-attr { color: #d19a66; }
.tok-str { color: #98c379; }
.tok-expr { color: #56b6c2; }
.tok-punc { color: #6b7280; }
.uo-output-label {
font-family: var(--mono-font); font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase;
color: var(--ink-soft); text-align: center;
}
.uo-preview { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; box-shadow: 0 10px 24px -12px rgba(26,26,26,0.18); }
.uo-preview-bar { background: #e9e9e9; padding: 0.45rem 0.6rem; display: flex; gap: 0.3rem; }
.uo-preview-bar span { width: 8px; height: 8px; border-radius: 50%; display: block; }
.uo-preview-bar span:nth-child(1) { background: #ff5f56; }
.uo-preview-bar span:nth-child(2) { background: #ffbd2e; }
.uo-preview-bar span:nth-child(3) { background: #27c93f; }
.uo-preview-page { background: #fff; padding: 1.8rem; text-align: center; }
.uo-real-btn {
font-family: var(--body-font); font-size: 0.9rem; font-weight: 600; color: #fff; background: var(--accent);
border: none; border-radius: 100px; padding: 0.6rem 1.2rem; cursor: pointer;
box-shadow: 0 6px 16px -4px rgba(37,99,235,0.45); transition: transform 0.15s;
}
.uo-real-btn:hover { transform: translateY(-1px); }
.uo-real-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
.infographic-ueout *, .infographic-ueout *::before, .infographic-ueout *::after {
transition-duration: 0.001ms !important; animation-duration: 0.001ms !important;
}
}
</style>
<div class="uo-grid">
<div class="uo-code-block">
<pre><code id="uoCode"></code></pre>
</div>
<p class="uo-output-label">OUTPUT</p>
<div class="uo-preview">
<div class="uo-preview-bar"><span></span><span></span><span></span></div>
<div class="uo-preview-page">
<button class="uo-real-btn" id="uoBtn" type="button">Clicked 0 times</button>
</div>
</div>
</div>
</div>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<script>
(function () {
var root = document.getElementById('uo');
if (!root) return;
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') dark = true;
else if (bodyAttr === 'false') dark = false;
else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) root.setAttribute('data-theme', 'dark');
window.addEventListener('message', function (e) {
if (e.data && e.data.type === 'darkMode') {
if (e.data.value) root.setAttribute('data-theme', 'dark');
else root.removeAttribute('data-theme');
}
});
var code =
'<span class="tok-kw">import</span> <span class="tok-punc">{</span> useState<span class="tok-punc">,</span> useEffect <span class="tok-punc">}</span> <span class="tok-kw">from</span> <span class="tok-str">"react"</span><span class="tok-punc">;</span>\n\n' +
'<span class="tok-kw">function</span> <span class="tok-type">Counter</span><span class="tok-punc">() {</span>\n' +
' <span class="tok-kw">const</span> <span class="tok-punc">[</span>count<span class="tok-punc">,</span> setCount<span class="tok-punc">]</span> <span class="tok-punc">=</span> useState<span class="tok-punc">(</span><span class="tok-expr">0</span><span class="tok-punc">);</span>\n\n' +
' useEffect<span class="tok-punc">(() => {</span>\n' +
' document.title <span class="tok-punc">=</span> <span class="tok-str">`Clicked ${count} times`</span><span class="tok-punc">;</span>\n' +
' <span class="tok-punc">},</span> <span class="tok-punc">[</span>count<span class="tok-punc">]);</span>\n\n' +
' <span class="tok-kw">return</span> <span class="tok-punc">(</span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">button</span> <span class="tok-attr">onClick</span><span class="tok-punc">={</span><span class="tok-punc">() =></span> setCount<span class="tok-punc">(</span>count <span class="tok-punc">+</span> <span class="tok-expr">1</span><span class="tok-punc">)}></span>\n' +
' Clicked <span class="tok-punc">{</span>count<span class="tok-punc">}</span> times\n' +
' <span class="tok-punc"></</span><span class="tok-tag">button</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc">);</span>\n' +
'<span class="tok-punc">}</span>\n\n' +
'<span class="tok-kw">export</span> <span class="tok-kw">default</span> <span class="tok-type">Counter</span><span class="tok-punc">;</span>';
root.querySelector('#uoCode').innerHTML = code;
var btn = root.querySelector('#uoBtn');
var count = 0;
btn.addEventListener('click', function () {
count += 1;
btn.textContent = 'Clicked ' + count + ' times';
document.title = 'Clicked ' + count + ' times'; // real side effect, same as the code's useEffect
});
})();
</script>🧠 What’s happening?
Runs on first render
Runs again only when
countchangesReplaces:
componentDidMountcomponentDidUpdate
👉 Example: Run Effect Only Once (componentDidMount)
🎯 Goal:
Log message only when component loads.
useEffect(() => {
console.log("Component mounted");
}, []);✔️ Runs only once
✔️ Similar to componentDidMount
🧹 Cleanup Example (componentWillUnmount)
🎯 Goal:
Start a timer and clear it when component unmounts.
useEffect(() => {
const timer = setInterval(() => {
console.log("Running...");
}, 2000);
return () => {
clearInterval(timer);
console.log("Cleanup done");
};
}, []);🧠 Explanation:
Timer starts when component mounts
Cleanup runs when component unmounts
Replaces
componentWillUnmount
🧠 Multiple useEffects (Best Practice)
👉 Use separate effects for unrelated logic
useEffect(() => {
document.title = "My App";
}, []);
useEffect(() => {
console.log("Count changed");
}, [count]);✔️ Cleaner
✔️ Easier to debug
✔️ Better separation of concerns
🔴 Common Mistakes to Avoid
❌ Missing dependencies
❌ Putting everything in one useEffect
❌ Forgetting cleanup for timers/listeners
❌ Creating infinite loops by updating state inside effect without proper dependencies
🚀 When Should You Use useEffect?
Use useEffect when you need to:
✔️ Fetch data after render
✔️ Update DOM manually
✔️ Add/remove event listeners
✔️ Start/stop timers
✔️ Subscribe/unsubscribe to services
❌ Don’t use it for pure UI logic (that belongs in render)
✅ Summary
useEffecthandles side effects in function componentsReplaces all major class lifecycle methods
Dependency array controls execution
Cleanup keeps your app memory-safe
Multiple effects = cleaner code