Week 4 - React Fundamentals

Why React?

Setting up a React project with Vite

Components

JSX

Props

Rendering lists

Conditional rendering

The mental model: how React renders (2)

Practice

Assignment

Front end Track

Conditional Rendering

Sometimes you want to show something only under certain conditions — a loading spinner while data is fetching, an error message when something goes wrong, a logout button only when the user is logged in. React has no special template syntax for this. You just use regular JavaScript inside {}.

There are four patterns worth knowing. Each fits a different situation.

https://www.youtube.com/watch?v=XvURBpFxdGw

Pattern 1: Ternary — Two Outcomes

{isLoggedIn ? <LogoutButton /> : <LoginButton />}

Use this when both branches matter — when you want to show one thing or another.

Pattern 2: Logical AND — One Outcome

{hasError && <ErrorMessage />}
{cartItems.length > 0 && <Checkout />}

Use this when you want to render something only if a condition is true, and nothing otherwise.

<aside> ⚠️

Warning

&& returns the left side if it's falsy. If that left side is the number 0, React renders the literal character "0" on screen:

{cartItems.length && <Checkout />}  // Renders "0" when the cart is empty

Always use an explicit boolean comparison:

{cartItems.length > 0 && <Checkout />}  // ✅

This is one of the most common gotchas in React. You'll hit it at least once.

</aside>

Pattern 3: Early Return — Whole-Component Branches

When a component has several different states to handle — loading, error, empty, success — early returns keep the code clean and readable:

function UserProfile({ user, isLoading, error }) {
  if (isLoading) return <Spinner />;
  if (error) return <ErrorMessage error={error} />;
  if (!user) return <p>No user found.</p>;

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.bio}</p>
    </div>
  );
}

Read top-to-bottom, return early on edge cases, render the happy path at the end. This is much cleaner than nesting ternaries inside the JSX.

<aside> 💡

Info

The loading/error/empty/success pattern you see above is the standard shape for any component that fetches data. You'll use it constantly once we get to data fetching in week 10 — the structure will already feel familiar.

</aside>

Pattern 4: Variable Assignment — Complex Logic

For switch-style logic with several possible values, compute the JSX before the return:

function StatusBadge({ status }) {
  let badge;

  if (status === 'online') {
    badge = <span style={{ color: 'green' }}>🟢 Online</span>;
  } else if (status === 'away') {
    badge = <span style={{ color: 'orange' }}>🟡 Away</span>;
  } else {
    badge = <span style={{ color: 'gray' }}>⚪ Offline</span>;
  }

  return <div>Status: {badge}</div>;
}

When there are many possible values, an object lookup is often cleaner:

const STATUS_BADGES: Record<string, JSX.Element> = {
  online: <span style={{ color: 'green' }}>🟢 Online</span>,
  away: <span style={{ color: 'orange' }}>🟡 Away</span>,
  offline: <span style={{ color: 'gray' }}>⚪ Offline</span>,
};

function StatusBadge({ status }: { status: string }) {
  return <div>Status: {STATUS_BADGES[status] ?? STATUS_BADGES.offline}</div>;
}

?? is the nullish coalescing operator — it falls back to the right side when the left side is null or undefined. Here it handles any status value that isn't in the map.

Returning null

A component can return null when it should render nothing at all:

function Notification({ message }: { message: string | null }) {
  if (!message) return null;
  return <div className="notification">{message}</div>;
}

The component still exists in the tree — it just produces no DOM output. This is different from not rendering the component at all, which matters when state or effects are involved (more on that in week 8).

<aside> ⌨️

Hands On

Build a WeatherCard component that takes a temperature prop typed as number | undefined. Render:

Use an early return for the no-data case and a variable for the temperature logic.

</aside>

Putting It Together

interface GreetingProps {
  isLoggedIn: boolean;
  username: string | null;
  unreadMessages: number;
}

function Greeting({ isLoggedIn, username, unreadMessages }: GreetingProps) {
  if (!username) return <p>Loading...</p>;

  return (
    <div>
      {isLoggedIn ? (
        <h2>Welcome back, {username}!</h2>
      ) : (
        <h2>Please log in.</h2>
      )}

      {isLoggedIn && unreadMessages > 0 && (
        <p>
          You have {unreadMessages} unread message{unreadMessages === 1 ? '' : 's'}.
        </p>
      )}

      {isLoggedIn && <button>Logout</button>}
    </div>
  );
}

Three patterns in one component: early return for the loading state, ternary for the two-outcome greeting, && for the conditional elements. Each used where it fits.

<aside> ⌨️

Hands On

Build a UserList component with three props: users (array), isLoading (boolean), error (string or null). Use early returns to handle each edge case:

Stretch: build a PriceTag component that takes price and an optional discount. With a discount, show the original price struck through and the discounted price beside it. Without, just show the price.

</aside>

<aside> 🎉

Celebration

You now have four tools for conditional rendering and you know when to reach for each one. Combined with lists and props, you can build nearly any static UI in React.

</aside>

Additional Resources

Reading

Interactive


The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

CC BY-NC-SA 4.0 Icons

Built with ❤️ by the HackYourFuture community · Thank you, contributors

Found a mistake or have a suggestion? Let us know in the feedback form.