Course Lessons

JAVASCRIPT FOUNDATIONS + AI MINDSET

Back to Course

Fetching Data in React

JAVASCRIPT... Lesson 24 of 41 8 min

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 

Loading Webbo3 articles...

; } // If there's an error, show error message if (error) { return
Error: {error}
; } // Success! Show the list of posts return ( <div style={{ padding: "40px" }}> <h1>Webbo3 Learning Feedh1> {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

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 
Loading...
; if (error) return
Error: {error}
; 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 
Loading...
; if (error) return
Error: {error.message}
; return
{data.map(...)}
; } // ========== 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
Loading...
; if (error) return
Error: {error.message}
; return
{data.map(...)}
; }

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

FeatureuseEffect + fetchReact Query
Code lengthLong (boilerplate)Short
CachingManualAutomatic
Background refetchingNot built inYes
Race condition safeNo (extra work)Yes
DevtoolsNoneExcellent
Best forSimple apps, learningProduction 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

Complete this lesson

Mark as complete to track your progress