React

Chapter 05: Rendering Lists & Conditional UI — Array mapping, reconciliation keys, index pitfall, ternaries, short-circuiting, and the zero falsy bug.

1. Rendering Lists with Array.prototype.map()

In standard JavaScript, we use for loops or .forEach() to iterate over data. In React JSX, we use .map() because it is a pure expression that transforms an array of raw data objects into a brand-new array of JSX elements.

💡 Real-World Analogy: Assembly Line Cookie Cutter

Think of an array of data as a continuous strip of cookie dough and .map() as your cookie cutter:

  • const rawData = ["Chocolate", "Vanilla", "Matcha"] $\rightarrow$ Raw ingredients.
  • rawData.map(flavor => <CookieCard flavor={flavor} />) $\rightarrow$ Each pass through the cutter produces a beautifully packaged, ready-to-display component!

Standard List Rendering Pattern

function TechStackList() {
    const skills = [
        { id: "s1", name: "JavaScript", level: "Advanced" },
        { id: "s2", name: "React 18", level: "Expert" },
        { id: "s3", name: "TypeScript", level: "Intermediate" }
    ];

    return (
        <div className="skills-container">
            <h3>Engineering Skills</h3>
            <ul className="skills-list">
                {skills.map((skill) => (
                    <li key={skill.id} className="skill-item">
                        <strong>{skill.name}</strong> — <span>{skill.level}</span>
                    </li>
                ))}
            </ul>
        </div>
    );
}
Rendered List Preview:

Engineering Skills

  • JavaScript — Advanced
  • React 18 — Expert
  • TypeScript — Intermediate

2. The key Prop & Why React Requires It

When rendering dynamic lists, React requires a unique key prop (usually an ID string or number) on the top-level element of each item.

How Keys Enable Lightning-Fast Reconciliation
key="a": Apple key="b": Banana Insert "Orange" at top key="o": Orange (✨ Inserted!) key="a": Apple (Preserved) key="b": Banana (Preserved)

🔍 What Happens Without Keys?

Without stable keys, if you insert an item at the beginning of an array, React assumes every single item shifted positions and forces a complete re-render and re-creation of all DOM nodes! With keys, React simply inserts the single new DOM node and leaves the rest untouched.

3. The "Array Index as Key" Anti-Pattern

Using the array index key={index} is a common beginner pitfall that can cause severe UI glitches when items are reordered, filtered, or deleted.

⚠️ When Array Index as Key Fails

If items contain internal state (like a text <input />, open accordion toggle, or checkbox), deleting the first item causes the second item to take over index 0. React thinks the first item is still there, and the user's typed text or checked checkbox will mistakenly stay on screen attached to the wrong item!

Rule of Thumb:
• Use database IDs (e.g., item.id) or UUIDs for keys (99% of cases).
• Use index only if the list is completely static, never reordered, filtered, or mutated.

4. The 4 Conditional Rendering Techniques

In React, there are 4 standard patterns to conditionally render elements depending on state or props:

01

1. If/Else with Early Return

Best for full-page states: Loading screens, Error boundaries, or Auth guards.

if (isLoading) return <Spinner />;
if (hasError) return <ErrorMessage />;
return <DashboardContent />;
02

2. Ternary Operator (? :)

Best for alternating between two distinct UI views inside JSX.

<button>
    {isSubscribed ? "Manage Billing" : "Upgrade to Pro"}
</button>
03

3. Logical AND (&&) Short-Circuit

Best when you want to render an element or render nothing at all.

{hasUnreadAlerts && (
    <span className="badge">New!</span>
)}
04

4. Object Lookup Maps

Best for multi-variant UI states (Status badges, Tabs, Themes).

const STATUS_BADGES = {
    pending: <YellowBadge />,
    shipped: <BlueBadge />,
    delivered: <GreenBadge />
};
return STATUS_BADGES[order.status] || null;

⚠️ The Famous "0 Falsy Number Trap" with Logical AND (&&)

In JavaScript, the expression 0 && <Component /> evaluates directly to the number 0! React renders numbers directly to the DOM, so your users will see a stray "0" floating on screen!

// ❌ BUG: If items.length is 0, this renders "0" on the webpage!
{items.length && <ItemList items={items} />}

// ✅ FIX 1: Explicit boolean comparison (> 0)
{items.length > 0 && <ItemList items={items} />}

// ✅ FIX 2: Double negation (!!) to cast to strict boolean
{!!items.length && <ItemList items={items} />}

// ✅ FIX 3: Ternary with explicit null
{items.length > 0 ? <ItemList items={items} /> : null}

5. Filtering & Sorting Lists Dynamically

Always keep your original array immutable in state, and chain .filter() and .sort() directly in the render function before calling .map():

function ProductCatalog({ products, selectedCategory, maxPrice }) {
    // 1. Filter products by category and budget
    const filteredProducts = products
        .filter(p => selectedCategory === "All" || p.category === selectedCategory)
        .filter(p => p.price <= maxPrice)
        .sort((a, b) => a.price - b.price); // Lowest price first

    // 2. Render filtered list or Empty State
    return (
        <div className="catalog-grid">
            {filteredProducts.length === 0 ? (
                <div className="empty-state">
                    <p>No products match your selected filters.</p>
                </div>
            ) : (
                filteredProducts.map(product => (
                    <div key={product.id} className="product-card">
                        <h4>{product.name}</h4>
                        <p className="price">${product.price}</p>
                    </div>
                ))
            )}
        </div>
    );
}
Rendered Filtered Grid Output:
Wireless Mouse

$29.99

Mechanical Keyboard

$79.00

6. Hands-On Practice Challenges

Sharpen your dynamic list rendering and conditional UI techniques with these hands-on challenges. Click "Solve Challenge" to open the code editor!

🎯 Hands-on Coding Challenge Array Mapping

Challenge 1: Array-to-JSX Element Transformer

Write a function renderSkillPills(skillsArray) that transforms an array of skill objects into an array of virtual element objects containing type, unique key, dynamic className, and formatted text content.

📋 Requirements & What to Write:
  • Map each item to an object with type: 'span'.
  • Attach key: skill.id || `skill-${index}`.
  • Format className: `pill pill-${skill.category.toLowerCase()}`.
  • Set content: `${skill.name} (${skill.exp} yrs)`.
Expected Result: [ { type: "span", key: "k1", className: "pill pill-frontend", content: "React (4 yrs)" }, ... ]
💡
Editor Instructions: Transform the array into virtual element objects using .map().
skillPills.js
Console / Terminal Output
// Output will appear here when you click "Run Code"...
🎯 Hands-on Coding Challenge Conditional UI Bug Fix

Challenge 2: Fix the "0 Falsy Number" Rendering Bug

Write a safe conditional helper safeShortCircuit(count, message) that returns the message when count > 0, but safely returns null when count === 0 without leaking the raw number 0.

📋 Requirements & What to Write:
  • If count > 0, return message.
  • If count <= 0, return strictly null.
  • Ensure the number 0 is never returned.
Expected Result: Test with count = 0: null | Test with count = 5: "You have alerts!"
💡
Editor Instructions: Ensure 0 never leaks by returning null when count $\le 0$.
safeShortCircuit.js
Console / Terminal Output
// Output will appear here when you click "Run Code"...
🎯 Hands-on Coding Challenge Pipelines & Sorting

Challenge 3: Multi-Criteria Filter & Sort Pipeline

Implement a data pipeline function filterAndSortProducts(products, criteria) that filters by category, filters by minRating, and sorts by price ('asc' or 'desc') immutably.

📋 Requirements & What to Write:
  • Filter by category (allow 'All' to match everything).
  • Filter products with rating >= minRating.
  • Sort by price ascending or descending based on sortOrder.
Expected Result: [ { id: 1, name: "Headphones", category: "Audio", price: 99, rating: 4.5 } ]
💡
Editor Instructions: Chain .filter() and .sort() immutably and click "▶ Run Code".
catalogPipeline.js
Console / Terminal Output
// Output will appear here when you click "Run Code"...