1️⃣ Why do we use useEffect for data fetching?
Fetching data from an API is a side effect.
Side effects:
Communicate with external servers
Are asynchronous
Should not block UI rendering
Fetching data directly inside a component body:
Runs on every render
Can cause infinite loops
useEffectallows us to control when the API call runs, usually:Once when the component mounts
2️⃣ What do we need?
useState→ store API datauseEffect→ run API call after renderAxios → make HTTP requests
Loader state → show loading status to users
import React, { useState, useEffect } from "react";
import axios from "axios";3️⃣ State setup
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);posts→ stores fetched dataloading→ controls loader visibility
4️⃣ Fetch data using useEffect
useEffectruns after the first renderEmpty dependency array (
[]) ensures:API call runs only once
Same behavior as
componentDidMount
useEffect(() => {
axios
.get("<https://jsonplaceholder.typicode.com/posts>")
.then(response => {
setPosts(response.data);
setLoading(false);
})
.catch(error => {
console.error("Error fetching posts:", error);
setLoading(false);
});
}, []);5️⃣ Rendering data with loader
While loading → show loader
After data loads → show posts list
{loading ? (
<p>Loading posts...</p>
) : (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)}6️⃣ Dependency array explained
Dependency Array | Behavior |
|---|---|
| Runs once on mount |
| Runs when |
❌ Omitted | Runs after every render (avoid for fetch) |
7️⃣ Common beginner mistakes ❌
Forgetting dependency array → infinite API calls
Fetching inside render or JSX
Adding
poststo dependency array unnecessarilyNot stopping loader on error
8️⃣ Simple execution flow
Component renders → loader shown
useEffectruns after renderAPI request is sent
Data received → state updated
Component re-renders → data displayed
🔚 In short
useEffect is the correct place for data fetching, and a loader improves user experience during async operations.
FULL WORKING EXAMPLE
<div class="infographic-postsout" id="po">
<style>
.infographic-postsout {
--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: 720px;
margin: 0 auto;
box-sizing: border-box;
background: transparent;
color: var(--ink);
font-family: var(--body-font);
}
.infographic-postsout[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--line: #2c313d;
--accent: #5b8def;
}
.infographic-postsout * { box-sizing: border-box; min-width: 0; }
.po-grid { display: flex; flex-direction: column; gap: 1.1rem; align-items: stretch; }
.po-code-block pre {
margin: 0; background: #0d0d10; border: 1px solid #24252d; border-radius: var(--radius);
padding: 0.9rem; overflow: auto; max-height: 400px;
}
.po-code-block code { font-family: var(--mono-font); font-size: 0.74rem; 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-punc { color: #6b7280; }
.tok-comment { color: #7f848e; font-style: italic; }
.po-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;
}
.po-preview { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; box-shadow: 0 10px 24px -12px rgba(26,26,26,0.18); }
.po-preview-bar { background: #e9e9e9; padding: 0.45rem 0.6rem; display: flex; gap: 0.3rem; }
.po-preview-bar span { width: 8px; height: 8px; border-radius: 50%; display: block; }
.po-preview-bar span:nth-child(1) { background: #ff5f56; }
.po-preview-bar span:nth-child(2) { background: #ffbd2e; }
.po-preview-bar span:nth-child(3) { background: #27c93f; }
.po-preview-page { background: #fff; padding: 1.5rem 1.6rem; color: #000; }
.po-preview-page h2 { margin: 0 0 0.9rem; font-size: 1.15rem; }
.po-loading { color: #666; font-size: 0.9rem; }
.po-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; max-height: 260px; overflow-y: auto; }
.po-list li {
font-size: 0.85rem; color: #222; padding: 0.5rem 0.65rem; background: #f7f7f5; border: 1px solid #e5e2da;
border-radius: 6px; opacity: 0; animation: po-fade 0.35s ease forwards;
}
@keyframes po-fade { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: translateY(0); } }
.po-error { color: #dc2626; font-size: 0.85rem; }
@media (prefers-reduced-motion: reduce) {
.infographic-postsout *, .infographic-postsout *::before, .infographic-postsout *::after {
transition-duration: 0.001ms !important; animation-duration: 0.001ms !important;
}
}
</style>
<div class="po-grid">
<div class="po-code-block">
<pre><code id="poCode"></code></pre>
</div>
<p class="po-output-label">OUTPUT</p>
<div class="po-preview">
<div class="po-preview-bar"><span></span><span></span><span></span></div>
<div class="po-preview-page">
<h2>Posts</h2>
<div id="poBody"><p class="po-loading">Loading posts...</p></div>
</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('po');
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-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' +
'<span class="tok-kw">import</span> axios <span class="tok-kw">from</span> <span class="tok-str">"axios"</span><span class="tok-punc">;</span>\n\n' +
'<span class="tok-kw">function</span> <span class="tok-type">PostsList</span><span class="tok-punc">() {</span>\n' +
' <span class="tok-kw">const</span> <span class="tok-punc">[</span>posts<span class="tok-punc">,</span> setPosts<span class="tok-punc">]</span> <span class="tok-punc">=</span> useState<span class="tok-punc">([]);</span>\n' +
' <span class="tok-kw">const</span> <span class="tok-punc">[</span>loading<span class="tok-punc">,</span> setLoading<span class="tok-punc">]</span> <span class="tok-punc">=</span> useState<span class="tok-punc">(</span><span class="tok-kw">true</span><span class="tok-punc">);</span>\n\n' +
' useEffect<span class="tok-punc">(() => {</span>\n' +
' axios\n' +
' .get<span class="tok-punc">(</span><span class="tok-str">"https://jsonplaceholder.typicode.com/posts"</span><span class="tok-punc">)</span>\n' +
' .then<span class="tok-punc">(</span>response <span class="tok-punc">=> {</span>\n' +
' setPosts<span class="tok-punc">(</span>response.data<span class="tok-punc">);</span>\n' +
' setLoading<span class="tok-punc">(</span><span class="tok-kw">false</span><span class="tok-punc">);</span>\n' +
' <span class="tok-punc">})</span>\n' +
' .catch<span class="tok-punc">(</span>error <span class="tok-punc">=> {</span>\n' +
' console.error<span class="tok-punc">(</span><span class="tok-str">"Error fetching posts:"</span><span class="tok-punc">,</span> error<span class="tok-punc">);</span>\n' +
' setLoading<span class="tok-punc">(</span><span class="tok-kw">false</span><span class="tok-punc">);</span>\n' +
' <span class="tok-punc">});</span>\n' +
' <span class="tok-punc">},</span> <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">div</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">h2</span><span class="tok-punc">></span>Posts<span class="tok-punc"></</span><span class="tok-tag">h2</span><span class="tok-punc">></span>\n\n' +
' <span class="tok-punc">{</span>loading <span class="tok-punc">? (</span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">p</span><span class="tok-punc">></span>Loading posts...<span class="tok-punc"></</span><span class="tok-tag">p</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc">) : (</span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">ul</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc">{</span>posts.map<span class="tok-punc">(</span>post <span class="tok-punc">=> (</span>\n' +
' <span class="tok-punc"><</span><span class="tok-tag">li</span> <span class="tok-attr">key</span><span class="tok-punc">={</span>post.id<span class="tok-punc">}>{</span>post.title<span class="tok-punc">}</</span><span class="tok-tag">li</span><span class="tok-punc">></span>\n' +
' <span class="tok-punc">))}</span>\n' +
' <span class="tok-punc"></</span><span class="tok-tag">ul</span><span class="tok-punc">></span>\n' +
' <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">PostsList</span><span class="tok-punc">;</span>';
root.querySelector('#poCode').innerHTML = code;
var out = root.querySelector('#poBody');
// sample data — standing in for what the API would normally return
var samplePosts = [
{ id: 1, title: 'sunt aut facere repellat provident occaecati excepturi optio' },
{ id: 2, title: 'qui est esse' },
{ id: 3, title: 'ea molestias quasi exercitationem repellat qui ipsa sit aut' },
{ id: 4, title: 'eum et est occaecati' },
{ id: 5, title: 'nesciunt quas odio' },
{ id: 6, title: 'dolorem eum magni eos aperiam quia' },
{ id: 7, title: 'magnam facilis autem' },
{ id: 8, title: 'dolorem dolore est ipsam' }
];
setTimeout(function () {
var ul = document.createElement('ul');
ul.className = 'po-list';
samplePosts.forEach(function (post, i) {
var li = document.createElement('li');
li.textContent = post.title;
li.style.animationDelay = (i * 60) + 'ms';
ul.appendChild(li);
});
out.innerHTML = '';
out.appendChild(ul);
}, 600);
})();
</script>