Foundations · Lesson 4/9

React — Think in Components

35 min 2 exercises 1 quiz

What you'll learn

  • The one idea behind React: components
  • What props and state mean (with toys, not jargon)
  • Rendering lists of components from an array — the pattern behind every feed, grid, and table
  • Why real apps need useEffect to fetch data
  • Write component-style code right here in the browser
  • Know exactly what to install and study next

React itself needs a build setup, so our exercise uses plain JavaScript to practice the React way of thinking — the skill that actually matters.

LEGO blocks 🧱

Look at any app — Twitter/X for example. You'll see the same pieces repeated everywhere: a tweet card, a like button, an avatar.

React's whole idea: build each piece once as a component, then assemble your app from those pieces like LEGO.

A React component is literally just a function that returns what it looks like:

function Welcome(props) {
  return <h1>Hello, {props.name}!</h1>;
}

// use it like an HTML tag — as many times as you want:
<Welcome name="Sara" />
<Welcome name="Omar" />

Two words to own:

  • Props = the knobs on the block. <Welcome name="Sara" /> passes name in — same block, different output. Props go in, the component can't change them.
  • State = the block's memory. A like button remembers "am I liked? how many likes?" When state changes, React automatically redraws that component. You never manually do element.textContent = ... again — that's the magic.
function LikeButton() {
  const [likes, setLikes] = useState(0);
  return <button onClick={() => setLikes(likes + 1)}>❤️ {likes}</button>;
}

useState(0) gives you the memory box and a setter. Call setLikes(...) and the button redraws itself. Compare that with the click counter you built in the JavaScript lesson — same result, but React does the DOM updating for you.

Exercise 1

Practice component thinking (in plain JS — no build tools needed):

Write a function UserCard(name, role) that returns an HTML string for one card: a <div class="card"> containing an <h3> with the name and a <p> with the role.

Then render three different cards into #app by calling it three times (the starter shows how the first call is joined in).

Lists — the pattern behind every feed and grid 📃

In the exercise above you called UserCard three times, by hand. Real apps don't know how many items they'll have — a feed might have 3 posts or 3,000. React's answer is .map(): transform an array of data into an array of components, in one line:

const users = [
  { name: "Sara", role: "Designer" },
  { name: "Omar", role: "Developer" },
  { name: "Lina", role: "Manager" },
];

function App() {
  return (
    <div>
      {users.map((u) => (
        <UserCard key={u.name} name={u.name} role={u.role} />
      ))}
    </div>
  );
}

Add a 4th person to the users array and a 4th card appears automatically — no new <UserCard> line needed. That key={u.name} prop is required: it's a unique ID that helps React track which card is which when the list changes (adds, removes, reorders) — always give list items a stable, unique key.

This single pattern — data array → .map() → components — is how you'll build almost every list, grid, or feed you ever make in React.

Exercise 2

Render from data, not by hand. Extend the card idea: instead of calling UserCard manually per person, build the HTML string by looping over the users array (plain JS for or .map() both work — this models what React's .map() does).

Requirements: render one .card per entry in users, each showing that person's name in an <h3>.

useEffect — fetching real data 🌐

Everything so far lives inside the browser. But real apps load data from a server — a list of products, a user's profile. React handles this with useEffect, which runs code after the component appears on screen:

function ProfilePage({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch("/api/users/" + userId)
      .then((res) => res.json())
      .then((data) => setUser(data));
  }, [userId]); // re-run whenever userId changes

  if (!user) return <p>Loading...</p>;
  return <h1>{user.name}</h1>;
}

Read the [userId] at the end as "only re-run this fetch if userId changes" — without it, React would re-fetch on every single render, hammering your server. The pattern is always the same: start with null/loading state, fetch in useEffect, call setUser when data arrives, and React re-renders automatically with the real data. This exact pattern is how this platform's own Profile page loads your GitHub stats after you sign in.

Real-world case study: this site's own cards 🔍

Every roadmap card, certification card, and course card on this platform is one component, rendered exactly like the exercise above: a data array (data/roadmaps.ts, data/certifications.ts) is .map()-ed into a row of cards. Add a new roadmap to that one data file, and a new card appears on the site automatically — nobody hand-writes a new <RoadmapCard> line for it. That's the entire payoff of thinking in components: change the data, not the UI code.

Quick quiz

Question 1 of 6

What is a React component?

Question 2 of 6

Props vs state — which is correct?

Question 3 of 6

What happens when you call setLikes(5)?

Question 4 of 6

Why did we return HTML from UserCard three times instead of copy-pasting the HTML three times?

Question 5 of 6

Why does React need a key prop when rendering a list with .map()?

Question 6 of 6

What does [userId] at the end of a useEffect call mean?

Your path into real React

You already think in components now. To write real React:

  1. Install Node.js (nodejs.org), then run: npm create vite@latest my-app -- --template react
  2. Follow the official tutorial at react.dev/learn — genuinely one of the best tutorials in software
  3. Rebuild the click counter and UserCard as real components with useState and props
  4. Practice .map()-ing a data array into a list of components, and fetching real data with useEffect
  5. The freeCodeCamp video in the Resources tab walks through everything on camera

Rule of thumb: if you catch yourself copy-pasting UI, that's a component waiting to be born. If you catch yourself writing the same component call for every item in a list, that's a .map() waiting to happen.