▶️ Live demo
Try it yourself — interact with the example below.
Loading demo…
🎯 What Are We Building?
Imagine you have a deck of question cards on a table. You flip one over to see the answer. When you flip a different card, the previous one flips back. If you flip the same card again, it closes.
THE FLASHCARD PATTERN:
┌─────────────────────────────────────────┐
│ 📚 DECK OF 5 CARDS ON A TABLE │
│ │
│ Card 1: "What is React?" │
│ Card 2: "What is a Component?" │
│ Card 3: "What is State?" │
│ Card 4: "What is JSX?" │
│ Card 5: "What is Props?" │
│ │
│ You can only see ONE answer at a time! │
│ │
│ Click Card 3 → Shows: "Memory of UI" │
│ Click Card 1 → Card 3 closes! │
│ Shows: "A JS library" │
│ Click Card 1 again → Closes! │
│ │
│ React needs to REMEMBER which card │
│ is currently flipped! │
└─────────────────────────────────────────┘
⚠️ The Big Problem: "How Does React Remember Which Card Is Open?"
// ==========================================
// THE "EVERYTHING STUCK" TRAP
// ==========================================
// ❌ WRONG: No state = React forgets immediately!
function BadFlashcards() {
// React has NO MEMORY of which card was clicked!
return (
<div>
{questions.map(q => (
<div key={q.id}>
<p>{q.question}</p>
{/* Where do we put the answer? Always show? */}
<p>{q.answer}</p> {/* All answers show! ❌ */}
</div>
))}
</div>
);
}
// Problems:
// 1. All answers show at once! (Messy!)
// 2. Clicking does nothing (no interactivity)
// 3. React doesn't know which card is "active"
// 4. No way to flip cards back and forth
// ==========================================
// THE SOLUTION: One State to Rule Them All
// ==========================================
// ✅ CORRECT: React remembers ONE selected ID
function GoodFlashcards() {
// Step 1: Create state (null = nothing selected)
const [selectedId, setSelectedId] = useState(null);
return (
<div className="flashcards">
{questions.map(question => (
<div
key={question.id}
className={question.id === selectedId ? "selected" : ""}
onClick={() => setSelectedId(question.id)}
>
{/*
Step 2: Use state to decide what to show
If this card's ID matches selected ID → show answer
Otherwise → show question
*/}
<p>
{question.id === selectedId
? question.answer // ← Flipped (show answer)
: question.question // ← Normal (show question)
}
</p>
</div>
))}
</div>
);
}
// What happens when user clicks Card 3 (id: 9103):
//
// 1. User clicks Card 3
// → onClick fires
// → setSelectedId(9103)
// → state updates to 9103
// → React re-renders ALL cards
//
// 2. React checks EACH card:
// Card 1: id=1000, selectedId=9103 → NOT equal → shows question
// Card 2: id=2000, selectedId=9103 → NOT equal → shows question
// Card 3: id=9103, selectedId=9103 → EQUAL! → shows answer ✅
// Card 4: id=4000, selectedId=9103 → NOT equal → shows question
// Card 5: id=5000, selectedId=9103 → NOT equal → shows question
//
// 3. Only Card 3 shows its answer! Perfect!
📋 The 3-Step Technique (Flashcard Edition)
┌─────────────────────────────────────────┐
│ THE 3-STEP TECHNIQUE FOR FLASHCARDS: │
│ │
│ Step 1: Create state (null = closed) │
│ const [selectedId, setSelectedId] = useState(null);│
│ │
│ Step 2: Use state to decide content │
│ {question.id === selectedId │
│ ? question.answer │
│ : question.question} │
│ │
│ Step 3: Update state on click │
│ onClick={() => setSelectedId(question.id)}│
│ │
│ Result: React OWNS which card is open! │
└─────────────────────────────────────────┘
🧠 Complete Visual Breakdown
The Data Structure
// questions.js
const questions = [
{
id: 9103,
question: "What is React?",
answer: "A JavaScript library for building user interfaces"
},
{
id: 2000,
question: "What is a Component?",
answer: "A reusable, independent piece of UI"
},
{
id: 3000,
question: "What is State?",
answer: "Memory that makes components interactive"
}
];
The State Box
┌─────────────────────────────────────────┐
│ REACT'S MEMORY BOX (useState) │
│ │
│ Initial State: │
│ ┌─────────────────┐ │
│ │ selectedId │ │
│ │ null │ ← "Nothing open" │
│ └─────────────────┘ │
│ │
│ After clicking Card 9103: │
│ ┌─────────────────┐ │
│ │ selectedId │ │
│ │ 9103 │ ← "Card 3 open" │
│ └─────────────────┘ │
│ │
│ After clicking Card 2000: │
│ ┌─────────────────┐ │
│ │ selectedId │ │
│ │ 2000 │ ← "Card 2 open" │
│ │ (9103 forgot!) │ │
│ └─────────────────┘ │
│ │
│ After clicking Card 2000 again: │
│ ┌─────────────────┐ │
│ │ selectedId │ │
│ │ null │ ← "Close it!" │
│ └─────────────────┘ │
└─────────────────────────────────────────┘
🔥 The Toggle Trick (The Hardest Part)
This is where most beginners get stuck. How do you close a card when you click it again?
// ==========================================
// THE TOGGLE LOGIC (Click Same = Close)
// ==========================================
// ❌ WRONG: Just sets the ID (can't close!)
onClick={() => setSelectedId(question.id)}
// If selectedId is 9103, and you click 9103 again:
// setSelectedId(9103) → state is already 9103!
// Nothing changes! Card stays open! ❌
// ✅ CORRECT: Toggle between ID and null
onClick={() => setSelectedId(
question.id === selectedId ? null : question.id
)}
// If selectedId is 9103, and you click 9103 again:
// question.id (9103) === selectedId (9103) → TRUE
// → setSelectedId(null) → closes card! ✓
// If selectedId is 9103, and you click 2000:
// question.id (2000) === selectedId (9103) → FALSE
// → setSelectedId(2000) → opens card 2! ✓
Visual Flow:
┌─────────────────────────────────────────┐
│ TOGGLE LOGIC FLOW CHART │
│ │
│ User clicks a card... │
│ ↓ │
│ Is this card already open? │
│ ↓ │
│ YES ──→ Close it (set to null) │
│ ↓ │
│ NO ──→ Open it (set to its ID) │
│ │
│ Like a light switch! │
│ Click ON → Light is ON │
│ Click same switch → Light is OFF │
└─────────────────────────────────────────┘
📦 Complete Code
Create file: Flashcards.jsx
import { useState } from "react";
// ==========================================
// THE DATA
// ==========================================
const questions = [
{
id: 9103,
question: "What is React?",
answer: "A JavaScript library for building user interfaces"
},
{
id: 2000,
question: "What is a Component?",
answer: "A reusable, independent piece of UI"
},
{
id: 3000,
question: "What is State?",
answer: "Memory that makes components interactive"
},
{
id: 4000,
question: "What is JSX?",
answer: "Syntax that looks like HTML but is JavaScript"
},
{
id: 5000,
question: "What is Props?",
answer: "Data passed from parent to child components"
}
];
// ==========================================
// THE COMPONENT
// ==========================================
export default function Flashcards() {
// Step 1: Create state
// null = no card is selected (all show questions)
const [selectedId, setSelectedId] = useState(null);
return (
<div className="flashcards">
{/*
Map over the array to create one card per question
Each card needs a unique key (question.id)
*/}
{questions.map((question) => (
<div
key={question.id}
/*
Step 2 (Part A): Conditional class
If this card is selected, add "selected" class
This changes the background color!
*/
className={question.id === selectedId ? "selected" : ""}
/*
Step 3: Update state on click
The TOGGLE logic:
- If already open → close (null)
- If closed → open (this card's ID)
*/
onClick={() =>
setSelectedId(
question.id === selectedId ? null : question.id
)
}
>
{/*
Step 2 (Part B): Conditional content
If this card's ID matches selectedId → show ANSWER
Otherwise → show QUESTION
*/}
<p>
{question.id === selectedId
? question.answer
: question.question}
</p>
</div>
))}
</div>
);
}
🎨 CSS (styles.css)
.flashcards {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 20px;
max-width: 800px;
margin: 40px auto;
font-family: Arial, sans-serif;
}
.flashcards div {
background-color: #e7e7e7;
border: 2px solid #ccc;
border-radius: 10px;
padding: 30px;
height: 120px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
cursor: pointer;
font-size: 18px;
transition: all 0.3s;
}
/* When the "selected" class is added */
.flashcards div.selected {
background-color: #e03131;
color: white;
border-color: #c92a2a;
font-weight: bold;
}
.flashcards div:hover {
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
🧠 Memory Aids for Poor Logic Thinking
The "TV Remote" Analogy
┌─────────────────────────────────────────────────┐
│ FLASHCARDS = TV WITH ONE REMOTE CONTROL │
│ │
│ UNCONTROLLED (No state): │
│ ┌─────────────────────────────────────────┐ │
│ │ 📺 5 TVs playing different channels │ │
│ │ │ │
│ │ Each TV has its own physical button │ │
│ │ You have no idea which one is on! │ │
│ │ All TVs could be on at once! │ │
│ │ Chaos! 😫 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ CONTROLLED (One state): │
│ ┌─────────────────────────────────────────┐ │
│ │ 📺 5 TVs, ONE REMOTE CONTROL │ │
│ │ │ │
│ │ Remote has ONE number in memory: │ │
│ │ "Currently watching channel 3" │ │
│ │ │ │
│ │ Press 3 → TV 3 turns ON (red) │ │
│ │ Press 1 → TV 3 turns OFF, TV 1 turns ON│ │
│ │ Press 1 again → TV 1 turns OFF │ │
│ │ │ │
│ │ Only ONE TV can be on at a time! │ │
│ │ Because remote only remembers ONE! │ │
│ └─────────────────────────────────────────┘ │
│ │
│ The Remote = selectedId state │
│ null = "No TV is on" │
│ 9103 = "TV 3 is on" │
└─────────────────────────────────────────────────┘
The "School Locker" Analogy
┌─────────────────────────────────────────────────┐
│ FLASHCARDS = ROW OF SCHOOL LOCKERS │
│ │
│ Imagine 5 lockers in a hallway: │
│ │
│ [1] [2] [3] [4] [5] │
│ 🔒 🔒 🔒 🔒 🔒 │
│ │
│ You have ONE master key that fits ONE locker. │
│ The key has a number engraved on it: │
│ │
│ Key says: "Opens locker #3" │
│ → Locker 3 is open, all others locked! │
│ │
│ You walk to locker #2 and try the key: │
│ → Key doesn't fit! │
│ → But a magic guard swaps the engraving! │
│ → Key now says: "Opens locker #2" │
│ → Locker 3 locks, Locker 2 opens! │
│ │
│ You walk back to locker #2: │
│ → Key fits! │
│ → But you turn it the other way... │
│ → Key now says: "Opens nothing" (null) │
│ → Locker 2 locks! │
│ │
│ In React: │
│ • selectedId = the number on the key │
│ • null = key has no number, all locked │
│ • setSelectedId = the magic guard re-engraves │
└─────────────────────────────────────────────────┘
The "Spotlight" Analogy
┌─────────────────────────────────────────────────┐
│ FLASHCARDS = STAGE WITH ONE SPOTLIGHT │
│ │
│ Imagine 5 actors on a stage: │
│ │
│ 🧑🎤 🧑🎤 🧑🎤 🧑🎤 🧑🎤 │
│ Actor Actor Actor Actor Actor │
│ 1 2 3 4 5 │
│ │
│ There is only ONE spotlight! │
│ The spotlight operator has a notebook: │
│ │
│ Notebook: "Shine on actor #3" │
│ → Actor 3 is lit up (red background) │
│ → All others in darkness │
│ │
│ Director shouts: "Actor 2!" │
│ → Operator writes: "Shine on actor #2" │
│ → Spotlight moves! Actor 2 lit, 3 dark! │
│ │
│ Director shouts: "Actor 2 again!" │
│ → Operator writes: "Shine on nobody" (null) │
│ → Spotlight turns OFF! All dark! │
│ │
│ In React: │
│ • selectedId = the notebook page number │
│ • null = spotlight off │
│ • setSelectedId = director's command │
│ • className = "selected" = the red spotlight │
└─────────────────────────────────────────────────┘
The "If/Then Decision Tree" Analogy
┌─────────────────────────────────────────────────┐
│ THE DECISION TREE IN EVERY CARD │
│ │
│ When React renders EACH card, it asks: │
│ │
│ ┌─────────────┐ │
│ │ Is my ID │ │
│ │ equal to │ │
│ │ selectedId? │ │
│ └──────┬──────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ │ │
│ YES NO │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Show │ │ Show │ │
│ │ ANSWER │ │ QUESTION │ │
│ │ (red) │ │ (gray) │ │
│ └──────────┘ └──────────┘ │
│ │
│ This happens for EVERY card, EVERY render! │
│ │
│ Card 1: id=1000, selectedId=9103 │
│ 1000 === 9103? NO → Question │
│ │
│ Card 3: id=9103, selectedId=9103 │
│ 9103 === 9103? YES → Answer │
└─────────────────────────────────────────────────┘
🎓 Practice Exercises
Exercise 1: Basic Flashcard List
Render a list of flashcards that shows questions (no click yet):
const data = [
{ id: 1, q: "What is HTML?", a: "HyperText Markup Language" },
{ id: 2, q: "What is CSS?", a: "Cascading Style Sheets" },
{ id: 3, q: "What is JS?", a: "JavaScript" }
];
function Flashcards() {
// TODO: Map over data and show questions
// TODO: Add keys!
// TODO: Add a className "card" to each div
return (
<div className="flashcards">
{/* Your code here */}
</div>
);
}
Solution:
function Flashcards() {
return (
<div className="flashcards">
{data.map(item => (
<div key={item.id} className="card">
<p>{item.q}</p>
</div>
))}
</div>
);
}
Exercise 2: Add the Toggle State
Add state so clicking a card shows its answer, and clicking again closes it:
function Flashcards() {
// TODO: Create selectedId state (null)
// TODO: Add onClick with toggle logic
// TODO: Show answer if selected, question if not
return (
<div className="flashcards">
{data.map(item => (
<div key={item.id} className="card">
{/* Show question or answer */}
</div>
))}
</div>
);
}
Solution:
import { useState } from "react";
function Flashcards() {
const [selectedId, setSelectedId] = useState(null);
return (
<div className="flashcards">
{data.map(item => (
<div
key={item.id}
className={item.id === selectedId ? "card selected" : "card"}
onClick={() =>
setSelectedId(item.id === selectedId ? null : item.id)
}
>
<p>{item.id === selectedId ? item.a : item.q}</p>
</div>
))}
</div>
);
}
Exercise 3: Fix the Broken Code
This code has 3 bugs. Find and fix them:
function Flashcards() {
const [selectedId, setSelectedId] = useState(0); // Bug 1?
return (
<div className="flashcards">
{questions.map(question => (
<div
className="selected" // Bug 2?
onClick={setSelectedId(question.id)} // Bug 3?
>
<p>{question.question}</p>
</div>
))}
</div>
);
}
Solution:
function Flashcards() {
// Fix 1: Use null (not 0) for "nothing selected"
const [selectedId, setSelectedId] = useState(null);
return (
<div className="flashcards">
{questions.map(question => (
<div
key={question.id} // Also missing: key prop!
// Fix 2: Conditional class, not always "selected"
className={question.id === selectedId ? "selected" : ""}
// Fix 3: Arrow function, not immediate call!
onClick={() => setSelectedId(
question.id === selectedId ? null : question.id
)}
>
{/* Fix 4: Show answer when selected! */}
<p>
{question.id === selectedId
? question.answer
: question.question}
</p>
</div>
))}
</div>
);
}
Exercise 4: Multiple Choice Quiz
Build a quiz where clicking an option shows if it's correct:
const questions = [
{
id: 1,
question: "What is 2 + 2?",
options: ["3", "4", "5"],
correct: 1 // index of correct answer
}
];
function Quiz() {
// TODO: State to track which option was clicked
// TODO: Show green if correct, red if wrong
// TODO: Only evaluate after clicking
return (
<div>
<h2>{questions[0].question}</h2>
{/* Render options */}
</div>
);
}
Solution:
import { useState } from "react";
function Quiz() {
const [selectedOption, setSelectedOption] = useState(null);
const q = questions[0];
return (
<div style={{ padding: "20px", textAlign: "center" }}>
<h2>{q.question}</h2>
{q.options.map((opt, index) => (
<button
key={index}
onClick={() => setSelectedOption(index)}
style={{
display: "block",
margin: "10px auto",
padding: "15px 30px",
fontSize: "18px",
border: "2px solid #ccc",
borderRadius: "8px",
cursor: "pointer",
backgroundColor:
selectedOption === null
? "white"
: index === q.correct
? "#e8f5e9" // green
: index === selectedOption
? "#ffebee" // red (wrong pick)
: "white"
}}
>
{opt}
{selectedOption !== null && index === q.correct && " ✅"}
</button>
))}
{selectedOption !== null && (
<p style={{ fontWeight: "bold" }}>
{selectedOption === q.correct
? "Correct! 🎉"
: "Wrong! Try again. ❌"}
</p>
)}
</div>
);
}
💡 Key Takeaways
| Concept | What It Means | Example |
|---|---|---|
| Single state for many items | One variable controls which item is "active" | const [selectedId, setSelectedId] = useState(null) |
| null as initial state | "Nothing is selected" | useState(null) not useState(0) |
| Conditional rendering | Show different content based on state | id === selectedId ? answer : question |
| Conditional class | Change styling based on state | className={id === selectedId ? "selected" : ""} |
| Toggle pattern | Click same = close, click different = switch | setSelectedId(id === selectedId ? null : id) |
| Arrow function in onClick | Pass function, don't call it! | onClick={() => handle(id)} |
| key prop | React needs unique IDs for lists | key={question.id} |
| map() | Transform array → JSX elements | array.map(item => <div>...</div>) |
The Flashcard Pattern:
function Flashcards() {
// 1. State (null = nothing open)
const [selectedId, setSelectedId] = useState(null);
return (
<div>
{items.map(item => (
<div
key={item.id}
// 2. Conditional class
className={item.id === selectedId ? "selected" : ""}
// 3. Toggle on click
onClick={() => setSelectedId(
item.id === selectedId ? null : item.id
)}
>
{/* 2. Conditional content */}
{item.id === selectedId ? item.answer : item.question}
</div>
))}
</div>
);
}
Golden Rules:
- Use
nullfor "nothing selected" — Not0, not"", notundefined - Always use
keyin lists — React needs it to track items - Never call functions directly in JSX —
onClick={func}notonClick={func()} - The toggle uses ternary —
condition ? null : valueis the magic - One state controls many — Like a remote control for multiple TVs
- Conditional rendering is powerful —
?and:decide what appears - React re-renders everything — When state changes, ALL cards check themselves
One Sentence Summary: > "The flashcard pattern uses a single piece of state called selectedId initialized to null to track which item is currently active, where you map over an array to render each item with a key prop and an onClick handler that uses a ternary toggle — setSelectedId(id === selectedId ? null : id) — to either close the current item or open a new one, while conditionally rendering different content and CSS classes by comparing each item's id to selectedId using the ternary operator, ensuring only one item can ever be active at a time!"