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>
);
}
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.
🔍 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:
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 />;
2. Ternary Operator (? :)
Best for alternating between two distinct UI views inside JSX.
<button>
{isSubscribed ? "Manage Billing" : "Upgrade to Pro"}
</button>
3. Logical AND (&&) Short-Circuit
Best when you want to render an element or render nothing at all.
{hasUnreadAlerts && (
<span className="badge">New!</span>
)}
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>
);
}
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!
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.
- 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)`.
[ { type: "span", key: "k1", className: "pill pill-frontend", content: "React (4 yrs)" }, ... ]
.map().
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.
- If
count > 0, returnmessage. - If
count <= 0, return strictlynull. - Ensure the number
0is never returned.
Test with count = 0: null | Test with count = 5: "You have alerts!"
0 never leaks by returning null when count $\le 0$.
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.
- Filter by
category(allow 'All' to match everything). - Filter products with
rating >= minRating. - Sort by price ascending or descending based on
sortOrder.
[ { id: 1, name: "Headphones", category: "Audio", price: 99, rating: 4.5 } ]
.filter() and .sort() immutably and click "▶ Run Code".