Fetching Data in React
From Static to Dynamic with API Calls
By AI Learning Assistant · React · useEffect · Fetch API · Custom Hooks
🤖 YOUR KIND AI LEARNING ASSISTANT
Hello learner! Today we're learning something really exciting: how to bring LIVE data into your React apps. Until now, we used hard‑coded data. But real apps show posts from servers, products from databases, messages from friends. I'll explain every concept in simple words, step by step. No rush take your time. You've got this! 💪
Why Fetch Data from an API?
FROM STATIC TO DYNAMIC APPLICATIONS
Imagine you're building a blog. You could write every article inside your code — but that would be terrible. Every time you publish a new article, you'd have to rewrite and redeploy the whole app. Instead, you store articles in a database and write one component that FETCHES them on demand. That's what APIs do — they give your app a way to ask for data.
In React, fetching data happens inside useEffect because it's a "side effect" — something that happens after the component renders. The browser needs to go out to the internet, ask a server for data, wait for the response, and then update the screen.
While waiting, your app should show a loading message. If something goes wrong (no internet, server down), show an error. When data arrives, display it. These three states — loading, error, success — are the heart of data fetching.
🎯 A SIMPLE ANALOGY
You order a pizza 🍕 (fetch data). The delivery person drives to the restaurant (API call). You wait at the door (loading state). If the pizza arrives, you eat (success). If the driver gets lost, you get a call saying sorry (error). Without these steps, you'd just stand there confused. That's why we show loading indicators and error messages!
How to Fetch Data Step by Step
fetch() · useEffect() · useState() for loading, error, data
We'll use a free fake API called JSONPlaceholder. It gives us fake blog posts for testing. No real data, just a playground.
Step 1 — Create state for the data, loading flag, and error
Step 2 — Inside useEffect (empty dependency array so it runs once), write an async function
Step 3 — Use fetch(url), await response, check if response.ok, then await response.json()
Step 4 — Update state (setData, setLoading(false)) and catch errors
Step 5 — Render different UI based on loading, error, or success
📜 CODE — Full Learning Feed Component (with detailed comments)
// ============================================ // Webbo3 Learning Feed — Fetches live blog posts // ============================================ import { useState, useEffect } from "react"; function App() { // State for the posts we fetch (initially empty array) const [posts, setPosts] = useState([]); // Loading state — true while fetching, false when done const [loading, setLoading] = useState(true); // Error state — null means no error, otherwise an error message const [error, setError] = useState(null); // This effect runs once when the component first appears useEffect(() => { // We need to define an async function inside (useEffect itself cannot be async) const fetchPosts = async () => { try { // Go to the internet and ask for data const response = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=8"); // If response is not OK (e.g., 404 Not Found, 500 Server Error), throw an error if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } // Convert the response from JSON format to a JavaScript array const data = await response.json(); // Save the posts to state setPosts(data); // Turn off loading — we have the data now setLoading(false); } catch (err) { // Something went wrong — save the error message setError(err.message); setLoading(false); } }; // Call the fetch function fetchPosts(); }, []); // Empty array = run only once when component mounts // While loading, show this if (loading) { return <h2 style={{ padding: "40px" }}>Loading Webbo3 articles...</h2>; } // If there's an error, show error message if (error) { return <div style={{ padding: "40px", color: "red" }}>Error: {error}</div>; } // Success! Show the list of posts return ( <div style={{ padding: "40px" }}> <h1>Webbo3 Learning Feed</h1> {posts.map(post => ( <div key={post.id} style={{ border: "1px solid #ccc", padding: "15px", marginBottom: "10px", borderRadius: "8px" }}> <h3>{post.title}</h3> <p>{post.body}</p> </div> ))} </div> ); } export default App;
🎯 Live Simulation — See Exactly How It Works
⏳ Loading Webbo3 articles...Webbo3 Learning Feed
Understanding React Hooks
Hooks let you use state and lifecycle in function components. Learn useState and useEffect today.
Building Your First Custom Hook
Extract reusable logic into custom hooks to keep components clean and testable.
Fetching Data with useEffect
Learn the three‑state pattern: loading, error, success.
React Query vs useEffect
Compare the built‑in approach with React Query for caching and background updates.
Styling React Apps
Explore CSS Modules, Styled Components, and Tailwind CSS.
✨ After 1.5 seconds (simulating network delay), the articles appear. This is exactly what your React app will do!
Making It Reusable — The useFetch Hook
DON'T REPEAT YOURSELF
If you fetch data in many components, you'll repeat the same boilerplate. React lets you create custom hooks — functions that start with "use" and can call other hooks. Here's a reusable useFetch hook.
// ============================================ // useFetch.js — A custom hook for any API call // ============================================ import { useState, useEffect } from "react"; function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchData = async () => { try { const res = await fetch(url); if (!res.ok) throw new Error(`Status ${res.status}`); const json = await res.json(); setData(json); } catch (err) { setError(err.message); } finally { setLoading(false); } }; fetchData(); }, [url]); // Re-run if the URL changes return { data, loading, error }; } export default useFetch; // ============================================ // Usage in any component becomes super clean! // ============================================ import useFetch from "./useFetch"; function App() { const { data: posts, loading, error } = useFetch("https://jsonplaceholder.typicode.com/posts?_limit=8"); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error}</div>; return ( <div> {posts.map(post => <PostCard key={post.id} title={post.title} body={post.body} />)} </div> ); }💬 ASK YOUR KIND AI ASSISTANT
"What is React Query and how does it improve data fetching?"
🗣️ YOU ASK THE AI:
"What is React Query and how does it improve data fetching compared to useEffect + fetch? Show me a side-by-side example."
🤗 YOUR KIND AI ASSISTANT RESPONDS (Simple Explanation):
What is React Query (also called TanStack Query)? It's a library that makes fetching, caching, and updating data in React apps much easier. Think of it as a smart assistant that handles all the boring details for you.
Problems with useEffect + fetch that React Query solves:
- Re‑fetching when you don't need to — With useEffect, if you visit a page, leave, and come back, it fetches the same data again. React Query caches data and reuses it.
- Loading/error boilerplate — You write the same loading/error code every time. React Query gives you `isLoading`, `error` automatically.
- No background updates — What if data changes on the server? React Query can auto‑refetch when the user refocuses the window or reconnects to the internet.
- Race conditions — If you quickly change a URL, old responses can overwrite new ones. React Query handles this automatically.
Side‑by‑side example — with useEffect vs with React Query
// ========== With useEffect + fetch (longer, manual) ========== function Todos() { const [data, setData] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { fetch("/api/todos") .then(res => res.json()) .then(data => { setData(data); setIsLoading(false); }) .catch(err => { setError(err); setIsLoading(false); }); }, []); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return <div>{data.map(...)}</div>; } // ========== With React Query (shorter, built‑in features) ========== import { useQuery } from "@tanstack/react-query"; function Todos() { const { data, isLoading, error } = useQuery({ queryKey: ["todos"], queryFn: () => fetch("/api/todos").then(res => res.json()), }); // isLoading, error, data are already provided! // Automatic caching, refetch on window focus, deduplication, devtools... if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return <div>{data.map(...)}</div>; }When should you use React Query? For medium to large apps with many API calls, caching needs, or real‑time updates. For tiny apps with 1‑2 simple fetches, useEffect is perfectly fine. Don't over‑complicate.
The key takeaway: React Query reduces boilerplate, prevents bugs, and gives you advanced features for free. It's used by companies like Google, Facebook, and Microsoft. But learn the basics first — you did the right thing by starting with useEffect!
📝 YOUR LEARNING JOURNAL
Today I learned that fetching data is like ordering a pizza — you need to wait, show that you're waiting, and handle mistakes. The three states (loading, error, success) make the user experience clear. I built a live feed component, then made it reusable with a custom hook. Finally, I discovered React Query — a library that does all the hard work for me. I feel ready to build real, data‑driven React apps. Next, I'll try adding a form to post new data! 🚀
Comparison — useEffect vs React Query
Feature useEffect + fetch React Query Code length Long (boilerplate) Short Caching Manual Automatic Background refetching Not built in Yes Race condition safe No (extra work) Yes Devtools None Excellent Best for Simple apps, learning Production apps, complex data 🤗 YOUR KIND AI ASSISTANT — ALWAYS HERE
💬 "My data shows up, then disappears!" → You might be resetting loading inside the effect incorrectly. Check your dependencies array.
💬 "I get a CORS error" → That means the API doesn't allow requests from your domain. For learning, use JSONPlaceholder (it allows all). For real APIs, you may need a backend proxy.
💬 "How do I POST data (send data to server)?" → Great question! You'll use fetch with method: "POST" and body: JSON.stringify(data). We'll cover that next week!
You made it through Day 24! Data fetching is a superpower — now you can connect your React apps to the entire internet. Celebrate every small win. You're doing amazing! 🎉
AI-Assisted JavaScript Learning · Fetching Data in React · From Static to Dynamic
Learn this with a real instructor
Free lessons take you a long way on your own. The full programmes add live classes, assignments, Skill Points and a certificate — built for African students.