🎯 What Is State? (Simple Analogy)
Imagine you're playing a video game:
PROPS (External Data - Like Game Instructions):
┌─────────────────────────────────────────┐
│ 📖 GAME MANUAL (Given to you) │
│ │
│ • Character name: "Hero" │
│ • Starting weapon: "Sword" │
│ • Game world: "Fantasy Land" │
│ │
│ These are SET when you start │
│ You CANNOT change them during gameplay │
│ They come from OUTSIDE (the game dev) │
│ │
│ In React: │
│ function Hero({ name, weapon }) { │
│ // name and weapon are props │
│ // They come from parent component │
│ // Hero cannot change them! │
│ } │
└─────────────────────────────────────────┘
STATE (Internal Data - Like Game Stats):
┌─────────────────────────────────────────┐
│ 🎮 CHARACTER STATS (Your memory) │
│ │
│ • Health: 100 → 80 → 50 → 0 │
│ • Score: 0 → 100 → 250 → 500 │
│ • Level: 1 → 2 → 3 │
│ • Inventory: [] → ["potion"] → ["potion", "key"]│
│ │
│ These CHANGE as you play │
│ The game REMEMBERS them over time │
│ They belong to YOUR character │
│ │
│ In React: │
│ function Game() { │
│ const [health, setHealth] = useState(100); │
│ const [score, setScore] = useState(0); │
│ // These are state! │
│ // Game can change them! │
│ // React remembers them! │
│ } │
└─────────────────────────────────────────┘
KEY INSIGHT:
Props = "Given to you, read-only"
State = "Your memory, can change, React remembers"
⚠️ The Big Problem: "Why Doesn't My Variable Change the UI?"
// ==========================================
// THE "VANILLA JS MENTALITY" TRAP
// ==========================================
// ❌ WRONG: Regular variable - React ignores changes!
function App() {
let count = 0; // Regular variable
function handleClick() {
count = count + 1; // Changing the variable
console.log(count); // Logs 1, 2, 3... BUT UI DOESN'T UPDATE!
}
return (
<div>
<p>Count: {count}</p> {/* Always shows 0! */}
<button onClick={handleClick}>Increment</button>
</div>
);
}
// What happens:
// 1. Component renders → count = 0 → UI shows "Count: 0"
// 2. User clicks → count becomes 1
// 3. But React DOESN'T KNOW count changed!
// 4. UI stays "Count: 0" forever
// 5. User clicks again → count becomes 2
// 6. UI still shows "Count: 0" 😫
// Visual:
// User clicks: count = 0 → 1 → 2 → 3
// UI shows: count = 0 → 0 → 0 → 0
// ↑
// React doesn't know to re-render!
// ==========================================
// THE SOLUTION: useState Hook
// ==========================================
// ✅ CORRECT: State variable - React tracks changes!
import { useState } from 'react';
function App() {
const [count, setCount] = useState(0); // State!
function handleClick() {
setCount(count + 1); // Tell React to update
// React KNOWS state changed!
// React RE-RENDERS the component!
// UI updates automatically!
}
return (
<div>
<p>Count: {count}</p> {/* Shows 0, then 1, then 2... */}
<button onClick={handleClick}>Increment</button>
</div>
);
}
// What happens:
// 1. Component renders → count = 0 → UI shows "Count: 0"
// 2. User clicks → setCount(1) called
// 3. React KNOWS state changed! 🎉
// 4. React RE-RENDERS the component
// 5. Component runs again → count = 1 → UI shows "Count: 1"
// 6. User clicks → setCount(2) called
// 7. React re-renders → UI shows "Count: 2"
// Visual:
// User clicks: setCount(1) → setCount(2) → setCount(3)
// React: Re-renders! → Re-renders! → Re-renders!
// UI shows: Count: 1 → Count: 2 → Count: 3
// ↑
// Magic! ✨
📋 Complete Basic Examples
Create file: react-state-basics.js
// ==========================================
// STATE IN REACT - Complete Guide
// ==========================================
/*
STATE = COMPONENT MEMORY
┌─────────────────────────────────────────┐
│ 1. Create state with useState() │
│ 2. State persists between renders │
│ 3. Updating state triggers re-render │
│ 4. React keeps UI in sync with state │
└─────────────────────────────────────────┘
*/
// ==========================================
// EXAMPLE 1: Basic Counter (The Classic)
// ==========================================
import { useState } from 'react';
function Counter() {
// Create state: [currentValue, updateFunction] = useState(initialValue)
const [count, setCount] = useState(0);
// count = 0 (current value)
// setCount = function to update count
// useState(0) = initial value is 0
function handleIncrement() {
setCount(count + 1); // Update state → React re-renders!
}
function handleDecrement() {
setCount(count - 1); // Update state → React re-renders!
}
return (
<div>
<h1>Count: {count}</h1>
<button onClick={handleDecrement}>-</button>
<button onClick={handleIncrement}>+</button>
</div>
);
}
// Visual Timeline:
// Initial render:
// count = 0
// UI: "Count: 0"
//
// User clicks +:
// setCount(0 + 1) → setCount(1)
// React: "State changed! Re-rendering!"
// count = 1
// UI: "Count: 1"
//
// User clicks + again:
// setCount(1 + 1) → setCount(2)
// React: "State changed! Re-rendering!"
// count = 2
// UI: "Count: 2"
// ==========================================
// EXAMPLE 2: Notification Badge
// ==========================================
function NotificationBell() {
const [count, setCount] = useState(5); // Start with 5 notifications
function handleRead() {
setCount(count - 1); // Decrease by 1
}
function handleClear() {
setCount(0); // Clear all
}
return (
<div>
<div className="bell">
🔔
{count > 0 && <span className="badge">{count}</span>}
</div>
<button onClick={handleRead}>Read One</button>
<button onClick={handleClear}>Clear All</button>
</div>
);
}
// State changes:
// Initial: count = 5 → Shows "🔔 5"
// Click Read One: count = 4 → Shows "🔔 4"
// Click Read One: count = 3 → Shows "🔔 3"
// Click Clear All: count = 0 → Shows "🔔" (no badge)
// ==========================================
// EXAMPLE 3: Input Field (Text State)
// ==========================================
function NameForm() {
const [name, setName] = useState(""); // Start empty
function handleChange(event) {
setName(event.target.value); // Update with input value
}
return (
<div>
<input
type="text"
value={name}
onChange={handleChange}
placeholder="Enter your name"
/>
<p>Hello, {name || "stranger"}!</p>
</div>
);
}
// State changes as user types:
// User types "A": name = "A" → "Hello, A!"
// User types "Al": name = "Al" → "Hello, Al!"
// User types "Ali": name = "Ali" → "Hello, Ali!"
// User types "Alice": name = "Alice" → "Hello, Alice!"
// ==========================================
// EXAMPLE 4: Toggle/Boolean State
// ==========================================
function ToggleButton() {
const [isOn, setIsOn] = useState(false); // Start OFF
function handleToggle() {
setIsOn(!isOn); // Flip the boolean
}
return (
<button
onClick={handleToggle}
style={{
backgroundColor: isOn ? '#4caf50' : '#f44336',
color: 'white'
}}
>
{isOn ? 'ON' : 'OFF'}
</button>
);
}
// State changes:
// Initial: isOn = false → Button is RED, says "OFF"
// Click: isOn = true → Button is GREEN, says "ON"
// Click: isOn = false → Button is RED, says "OFF"
// Click: isOn = true → Button is GREEN, says "ON"
// ==========================================
// EXAMPLE 5: Steps Component (From Tutorial)
// ==========================================
function Steps() {
const [step, setStep] = useState(1); // Start at step 1
const [isOpen, setIsOpen] = useState(true); // Panel is open
const messages = [
"Learn React ⚛️",
"Apply for jobs 💼",
"Invest your new income 🤑"
];
function handlePrevious() {
if (step > 1) setStep(step - 1);
}
function handleNext() {
if (step < 3) setStep(step + 1);
}
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>
{isOpen ? 'Close' : 'Open'}
</button>
{isOpen && (
<div className="steps">
<div className="numbers">
<div className={step >= 1 ? 'active' : ''}>1</div>
<div className={step >= 2 ? 'active' : ''}>2</div>
<div className={step >= 3 ? 'active' : ''}>3</div>
</div>
<p className="message">
Step {step}: {messages[step - 1]}
</p>
<div className="buttons">
<button onClick={handlePrevious}>Previous</button>
<button onClick={handleNext}>Next</button>
</div>
</div>
)}
</div>
);
}
// State changes when clicking Next:
// Initial: step = 1, isOpen = true
// → Shows step 1 highlighted, message "Learn React"
//
// Click Next: setStep(2)
// → step = 2, re-render!
// → Shows steps 1 & 2 highlighted, message "Apply for jobs"
//
// Click Next: setStep(3)
// → step = 3, re-render!
// → Shows all steps highlighted, message "Invest income"
//
// Click Close: setIsOpen(false)
// → isOpen = false, re-render!
// → Panel disappears!
🚀 Interactive React Usage Examples
Complete React File: StateMasterClass.jsx
import React, { useState } from 'react';
// ==========================================
// INTERACTIVE STATE DEMO
// ==========================================
function StateMasterClass() {
const [activeDemo, setActiveDemo] = useState('counter');
const demos = {
counter: { title: 'Counter', component: <CounterDemo /> },
memory: { title: 'Memory Game', component: <MemoryDemo /> },
form: { title: 'Form Input', component: <FormDemo /> },
multiple: { title: 'Multiple States', component: <MultipleStateDemo /> }
};
return (
<div style={{ maxWidth: '900px', margin: '0 auto', padding: '20px', fontFamily: 'Arial, sans-serif' }}>
<header style={{ background: '#e9c46a', color: '#264653', padding: '30px', borderRadius: '10px', marginBottom: '30px' }}>
<h1 style={{ margin: 0 }}>🧠 State: React's Memory</h1>
<p style={{ margin: '10px 0 0 0', opacity: 0.9 }}>How React Remembers and Updates</p>
</header>
<nav style={{ display: 'flex', gap: '10px', marginBottom: '30px', flexWrap: 'wrap' }}>
{Object.entries(demos).map(([key, { title }]) => (
<button
key={key}
onClick={() => setActiveDemo(key)}
style={{
padding: '12px 24px',
fontSize: '16px',
border: 'none',
borderRadius: '8px',
cursor: 'pointer',
backgroundColor: activeDemo === key ? '#264653' : '#e0e0e0',
color: activeDemo === key ? 'white' : '#333',
fontWeight: activeDemo === key ? 'bold' : 'normal'
}}
>
{title}
</button>
))}
</nav>
<main style={{ background: '#f8f9fa', padding: '30px', borderRadius: '10px', minHeight: '400px' }}>
{demos[activeDemo].component}
</main>
</div>
);
}
// ==========================================
// DEMO 1: Counter with Explanation
// ==========================================
function CounterDemo() {
const [count, setCount] = useState(0);
const [history, setHistory] = useState([]);
function handleIncrement() {
const newCount = count + 1;
setCount(newCount);
setHistory(prev => [...prev, { action: '+', value: newCount, time: new Date().toLocaleTimeString() }]);
}
function handleDecrement() {
const newCount = count - 1;
setCount(newCount);
setHistory(prev => [...prev, { action: '-', value: newCount, time: new Date().toLocaleTimeString() }]);
}
function handleReset() {
setCount(0);
setHistory([]);
}
return (
<div>
<h2>Counter: State in Action</h2>
<div style={{ display: 'grid', gap: '20px', gridTemplateColumns: '1fr 1fr' }}>
{/* Counter Display */}
<div style={{ padding: '20px', background: 'white', borderRadius: '8px', border: '2px solid #e0e0e0' }}>
<h3>Current State</h3>
<div style={{
fontSize: '60px',
textAlign: 'center',
padding: '20px',
background: '#f5f5f5',
borderRadius: '8px',
marginBottom: '15px'
}}>
{count}
</div>
<div style={{ display: 'flex', gap: '10px', justifyContent: 'center' }}>
<button
onClick={handleDecrement}
style={{ padding: '10px 20px', fontSize: '20px', background: '#f44336', color: 'white', border: 'none', borderRadius: '5px' }}
>
-
</button>
<button
onClick={handleReset}
style={{ padding: '10px 20px', fontSize: '16px', background: '#9e9e9e', color: 'white', border: 'none', borderRadius: '5px' }}
>
Reset
</button>
<button
onClick={handleIncrement}
style={{ padding: '10px 20px', fontSize: '20px', background: '#4caf50', color: 'white', border: 'none', borderRadius: '5px' }}
>
+
</button>
</div>
<div style={{ marginTop: '15px', padding: '10px', background: '#e3f2fd', borderRadius: '4px', fontSize: '14px' }}>
<strong>How it works:</strong>
<ol style={{ margin: '5px 0', paddingLeft: '20px' }}>
<li>User clicks button</li>
<li>setCount(newValue) called</li>
<li>React detects state change</li>
<li>React re-renders component</li>
<li>UI updates with new count!</li>
</ol>
</div>
</div>
{/* State History */}
<div style={{ padding: '20px', background: 'white', borderRadius: '8px', border: '2px solid #e0e0e0' }}>
<h3>State Change History</h3>
<div style={{ maxHeight: '250px', overflow: 'auto', background: '#1e1e1e', padding: '10px', borderRadius: '4px' }}>
{history.length === 0 ? (
<p style={{ color: '#666', fontStyle: 'italic' }}>No changes yet. Click + or -!</p>
) : (
history.map((h, i) => (
<div key={i} style={{ color: '#d4d4d4', fontSize: '13px', marginBottom: '4px', fontFamily: 'monospace' }}>
<span style={{ color: '#4caf50' }}>{h.time}</span>
{' '}
<span style={{ color: h.action === '+' ? '#4caf50' : '#f44336' }}>{h.action}</span>
{' → '}
<span style={{ color: '#2196f3' }}>{h.value}</span>
</div>
))
)}
</div>
</div>
</div>
</div>
);
}
// ==========================================
// DEMO 2: Memory Game (State Persistence)
// ==========================================
function MemoryDemo() {
const [flippedCards, setFlippedCards] = useState([]);
const [matchedPairs, setMatchedPairs] = useState(0);
const [moves, setMoves] = useState(0);
const cards = [
{ id: 1, emoji: '🍎', pair: 'A' },
{ id: 2, emoji: '🍌', pair: 'B' },
{ id: 3, emoji: '🍎', pair: 'A' },
{ id: 4, emoji: '🍌', pair: 'B' },
];
function handleCardClick(id) {
if (flippedCards.includes(id)) return;
const newFlipped = [...flippedCards, id];
setFlippedCards(newFlipped);
setMoves(m => m + 1);
if (newFlipped.length === 2) {
const firstCard = cards.find(c => c.id === newFlipped[0]);
const secondCard = cards.find(c => c.id === newFlipped[1]);
if (firstCard.pair === secondCard.pair) {
setMatchedPairs(p => p + 1);
}
setTimeout(() => setFlippedCards([]), 1000);
}
}
function handleReset() {
setFlippedCards([]);
setMatchedPairs(0);
setMoves(0);
}
return (
<div>
<h2>Memory Game: Multiple States</h2>
<div style={{ marginBottom: '15px', display: 'flex', gap: '20px', alignItems: 'center' }}>
<span>Moves: <strong>{moves}</strong></span>
<span>Matched: <strong>{matchedPairs}/2</strong></span>
<button onClick={handleReset} style={{ padding: '5px 15px' }}>Reset Game</button>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px', maxWidth: '200px' }}>
{cards.map(card => (
<div
key={card.id}
onClick={() => handleCardClick(card.id)}
style={{
width: '80px',
height: '80px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '30px',
backgroundColor: flippedCards.includes(card.id) ? '#e3f2fd' : '#7950f2',
color: flippedCards.includes(card.id) ? '#333' : 'white',
borderRadius: '8px',
cursor: 'pointer',
transition: 'all 0.3s'
}}
>
{flippedCards.includes(card.id) ? card.emoji : '?'}
</div>
))}
</div>
<div style={{ marginTop: '15px', padding: '10px', background: '#fff3e0', borderRadius: '4px', fontSize: '14px' }}>
<strong>States being tracked:</strong>
<ul>
<li><code>flippedCards</code>: Which cards are face up</li>
<li><code>matchedPairs</code>: How many pairs found</li>
<li><code>moves</code>: Total moves made</li>
</ul>
</div>
</div>
);
}
// ==========================================
// DEMO 3: Form Input (Text State)
// ==========================================
function FormDemo() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [isSubscribed, setIsSubscribed] = useState(false);
return (
<div>
<h2>Form State: Tracking Inputs</h2>
<div style={{ maxWidth: '400px', padding: '20px', background: 'white', borderRadius: '8px', border: '2px solid #e0e0e0' }}>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>Name:</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
style={{ width: '100%', padding: '8px', borderRadius: '4px', border: '1px solid #ccc' }}
placeholder="Enter name"
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>Email:</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
style={{ width: '100%', padding: '8px', borderRadius: '4px', border: '1px solid #ccc' }}
placeholder="Enter email"
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
<input
type="checkbox"
checked={isSubscribed}
onChange={(e) => setIsSubscribed(e.target.checked)}
/>
Subscribe to newsletter
</label>
</div>
<div style={{ padding: '15px', background: '#f5f5f5', borderRadius: '4px' }}>
<h4 style={{ margin: '0 0 10px 0' }}>Live Preview:</h4>
<p><strong>Name:</strong> {name || '(empty)'}</p>
<p><strong>Email:</strong> {email || '(empty)'}</p>
<p><strong>Subscribed:</strong> {isSubscribed ? 'Yes ✅' : 'No ❌'}</p>
</div>
</div>
<div style={{ marginTop: '15px', padding: '10px', background: '#e8f5e9', borderRadius: '4px', fontSize: '14px' }}>
<strong>Key Concept:</strong> Every keystroke updates state → React re-renders → UI stays in sync!
</div>
</div>
);
}
// ==========================================
// DEMO 4: Multiple State Variables
// ==========================================
function MultipleStateDemo() {
const [step, setStep] = useState(1);
const [isOpen, setIsOpen] = useState(true);
const [theme, setTheme] = useState('light');
const messages = [
"Learn React ⚛️",
"Apply for jobs 💼",
"Invest your new income 🤑"
];
const themes = {
light: { bg: '#f8f9fa', text: '#333', card: 'white' },
dark: { bg: '#1a1a2e', text: '#eee', card: '#16213e' },
colorful: { bg: '#fff3e0', text: '#e65100', card: '#ffe0b2' }
};
const currentTheme = themes[theme];
return (
<div style={{ backgroundColor: currentTheme.bg, color: currentTheme.text, padding: '20px', borderRadius: '8px' }}>
<h2>Multiple States: Steps + Theme + Visibility</h2>
<div style={{ marginBottom: '15px', display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
<button onClick={() => setIsOpen(!isOpen)}>
{isOpen ? 'Close Panel' : 'Open Panel'}
</button>
<button onClick={() => setTheme('light')}>Light Theme</button>
<button onClick={() => setTheme('dark')}>Dark Theme</button>
<button onClick={() => setTheme('colorful')}>Colorful Theme</button>
</div>
{isOpen && (
<div style={{
padding: '20px',
backgroundColor: currentTheme.card,
borderRadius: '8px',
border: '2px solid',
borderColor: theme === 'dark' ? '#0f3460' : '#e0e0e0'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '20px' }}>
{[1, 2, 3].map(num => (
<div key={num} style={{
width: '40px',
height: '40px',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontWeight: 'bold',
backgroundColor: step >= num ? '#7950f2' : '#e0e0e0',
color: step >= num ? 'white' : '#333'
}}>
{num}
</div>
))}
</div>
<p style={{ textAlign: 'center', fontSize: '18px', marginBottom: '20px' }}>
Step {step}: {messages[step - 1]}
</p>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<button
onClick={() => setStep(s => Math.max(1, s - 1))}
disabled={step === 1}
>
Previous
</button>
<button
onClick={() => setStep(s => Math.min(3, s + 1))}
disabled={step === 3}
>
Next
</button>
</div>
</div>
)}
<div style={{ marginTop: '15px', padding: '10px', background: 'rgba(0,0,0,0.1)', borderRadius: '4px', fontSize: '14px' }}>
<strong>Active States:</strong>
<ul style={{ margin: '5px 0' }}>
<li><code>step</code>: {step}</li>
<li><code>isOpen</code>: {isOpen.toString()}</li>
<li><code>theme</code>: {theme}</li>
</ul>
</div>
</div>
);
}
export default StateMasterClass;
🧠 Memory Aids for Poor Logic Thinking
The "Whiteboard" Analogy
┌─────────────────────────────────────────────────┐
│ STATE = WHITEBOARD IN A CLASSROOM │
│ │
│ 📋 PROPS = Printed handout (given to you) │
│ • You can read it │
│ • You CANNOT change it │
│ • Teacher can give you a new one │
│ │
│ 📝 STATE = Whiteboard (your memory) │
│ • You can write on it │
│ • You can erase and rewrite │
│ • Everyone sees the current version │
│ • When you change it, everyone sees the update │
│ │
│ REACT COMPONENT = Classroom │
│ ┌─────────────────────────────────────────┐ │
│ │ Teacher gives handout: props │ │
│ │ Student writes on whiteboard: state │ │
│ │ │ │
│ │ When student erases and writes: │ │
│ │ setState(newValue) │ │
│ │ ↓ │ │
│ │ Everyone looks at whiteboard again: │ │
│ │ React re-renders! │ │
│ └─────────────────────────────────────────┘ │
│ │
│ KEY RULE: │
│ Changing the whiteboard (state) makes everyone │
│ look at it again (re-render) │
│ Changing the handout (props) requires teacher │
│ to give a new one (parent re-renders) │
└─────────────────────────────────────────────────┘
The "Thermostat" Analogy
┌─────────────────────────────────────────────────┐
│ STATE = THERMOSTAT │
│ │
│ REGULAR VARIABLE (Broken Thermostat): │
│ ┌─────────────────────────────────────────┐ │
│ │ let temperature = 70; │ │
│ │ │ │
│ │ function makeWarmer() { │ │
│ │ temperature = 75; // You changed it │ │
│ │ // But the heater doesn't know! │ │
│ │ } │ │
│ │ │ │
│ │ Display still shows: 70°F │ │
│ │ Heater still outputs: 70°F air │ │
│ │ ❌ Out of sync! │ │
│ └─────────────────────────────────────────┘ │
│ │
│ STATE (Smart Thermostat): │
│ ┌─────────────────────────────────────────┐ │
│ │ const [temp, setTemp] = useState(70); │ │
│ │ │ │
│ │ function makeWarmer() { │ │
│ │ setTemp(75); // Tell React to update│ │
│ │ // React notifies everyone! │ │
│ │ } │ │
│ │ │ │
│ │ Display updates to: 75°F │ │
│ │ Heater adjusts to: 75°F air │ │
│ │ ✅ Everything in sync! │ │
│ └─────────────────────────────────────────┘ │
│ │
│ setTemp(75) = "Hey React, temperature changed!" │
│ React = "Got it! I'll tell everyone to update!" │
└─────────────────────────────────────────────────┘
State vs Props Comparison
┌─────────────────────────────────────────────────┐
│ STATE vs PROPS - SIDE BY SIDE │
│ │
│ ┌─────────────────┬─────────────────────────┐ │
│ │ PROPS │ STATE │ │
│ ├─────────────────┼─────────────────────────┤ │
│ │ From OUTSIDE │ From INSIDE │ │
│ │ (parent) │ (component itself) │ │
│ ├─────────────────┼─────────────────────────┤ │
│ │ Read-only │ Read AND write │ │
│ │ (immutable) │ (mutable) │ │
│ ├─────────────────┼─────────────────────────┤ │
│ │ Parent changes │ Component changes │ │
│ │ them │ them │ │
│ ├─────────────────┼─────────────────────────┤ │
│ │ Used to │ Used to make │ │
│ │ configure │ interactive │ │
│ ├─────────────────┼─────────────────────────┤ │
│ │ Like function │ Like component's │ │
│ │ arguments │ memory │ │
│ └─────────────────┴─────────────────────────┘ │
│ │
│ VISUAL: │
│ ┌─────────────────────────────────────────┐ │
│ │ Parent Component │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ <Child name="Alice" age={25} /> │ │ │
│ │ │ ↑ ↑ │ │ │
│ │ │ PROPS passed down │ │ │
│ │ └─────────────────────────────────┘ │ │
│ │ ↓ │ │
│ │ Child Component │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ function Child({ name, age }) { │ │ │
│ │ │ const [count, setCount] = │ │ │
│ │ │ useState(0); ← STATE │ │ │
│ │ │ │ │ │
│ │ │ // Can read props │ │ │
│ │ │ // Can read/write state │ │ │
│ │ │ } │ │ │
│ │ └─────────────────────────────────┘ │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
The Re-Render Cycle
┌─────────────────────────────────────────────────┐
│ HOW STATE TRIGGERS RE-RENDER │
│ │
│ INITIAL RENDER: │
│ ┌─────────────────────────────────────────┐ │
│ │ 1. Component function runs │ │
│ │ 2. useState(0) → count = 0 │ │
│ │ 3. JSX returned with count = 0 │ │
│ │ 4. React paints UI: "Count: 0" │ │
│ └─────────────────────────────────────────┘ │
│ ↓ │
│ USER CLICKS BUTTON: │
│ ┌─────────────────────────────────────────┐ │
│ │ 1. handleClick() runs │ │
│ │ 2. setCount(1) called │ │
│ │ 3. React: "State changed!" 🚨 │ │
│ └─────────────────────────────────────────┘ │
│ ↓ │
│ RE-RENDER: │
│ ┌─────────────────────────────────────────┐ │
│ │ 1. Component function runs AGAIN │ │
│ │ 2. useState(0) → IGNORED, uses 1 │ │
│ │ 3. JSX returned with count = 1 │ │
│ │ 4. React updates UI: "Count: 1" │ │
│ └─────────────────────────────────────────┘ │
│ ↓ │
│ USER CLICKS AGAIN: │
│ ┌─────────────────────────────────────────┐ │
│ │ 1. handleClick() runs │ │
│ │ 2. setCount(2) called │ │
│ │ 3. React: "State changed!" 🚨 │ │
│ └─────────────────────────────────────────┘ │
│ ↓ │
│ RE-RENDER AGAIN: │
│ ┌─────────────────────────────────────────┐ │
│ │ 1. Component function runs AGAIN │ │
│ │ 2. useState(0) → IGNORED, uses 2 │ │
│ │ 3. JSX returned with count = 2 │ │
│ │ 4. React updates UI: "Count: 2" │ │
│ └─────────────────────────────────────────┘ │
│ │
│ KEY INSIGHT: │
│ useState(initial) only uses initial value │
│ on FIRST render. After that, React remembers │
│ the current value! │
└─────────────────────────────────────────────────┘
🎓 Practice Exercises
Exercise 1: Create a Like Button
Create a like button that toggles between liked and not liked:
function LikeButton() {
// TODO: Add state for liked status
// TODO: Toggle between 🤍 and ❤️
// TODO: Show "Like" or "Liked" text
return (
<button>
{/* Your code here */}
</button>
);
}
Solution:
import { useState } from 'react';
function LikeButton() {
const [isLiked, setIsLiked] = useState(false);
function handleToggle() {
setIsLiked(!isLiked);
}
return (
<button
onClick={handleToggle}
style={{
fontSize: '20px',
padding: '10px 20px',
backgroundColor: isLiked ? '#ffebee' : 'white',
border: '1px solid #ccc',
borderRadius: '20px',
cursor: 'pointer'
}}
>
{isLiked ? '❤️ Liked' : '🤍 Like'}
</button>
);
}
Exercise 2: Fix the Bug
This code doesn't update the UI when clicking:
function Counter() {
let count = 0;
function handleClick() {
count = count + 1;
}
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Add</button>
</div>
);
}
Bug: Using regular variable instead of state.
Solution:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Add</button>
</div>
);
}
Exercise 3: Create a Todo Counter
Create a simple todo counter with add and complete functionality:
function TodoCounter() {
// TODO: Track total todos and completed todos
// TODO: Add buttons to add todo and complete todo
// TODO: Show "X of Y completed"
return (
<div>
{/* Your code here */}
</div>
);
}
Solution:
import { useState } from 'react';
function TodoCounter() {
const [total, setTotal] = useState(0);
const [completed, setCompleted] = useState(0);
function handleAdd() {
setTotal(t => t + 1);
}
function handleComplete() {
if (completed < total) {
setCompleted(c => c + 1);
}
}
function handleReset() {
setTotal(0);
setCompleted(0);
}
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h2>Todo Progress</h2>
<p style={{ fontSize: '24px' }}>
{completed} of {total} completed
</p>
<div style={{ display: 'flex', gap: '10px', justifyContent: 'center' }}>
<button onClick={handleAdd}>Add Todo</button>
<button onClick={handleComplete}>Complete One</button>
<button onClick={handleReset}>Reset</button>
</div>
<div style={{ marginTop: '20px' }}>
{total > 0 && (
<div style={{
width: '100%',
height: '20px',
backgroundColor: '#e0e0e0',
borderRadius: '10px'
}}>
<div style={{
width: `${(completed / total) * 100}%`,
height: '100%',
backgroundColor: '#4caf50',
borderRadius: '10px',
transition: 'width 0.3s'
}} />
</div>
)}
</div>
</div>
);
}
💡 Key Takeaways
| Concept | What It Means | Example |
|---|---|---|
| State | Component's memory that persists over time | const [count, setCount] = useState(0) |
| useState | Hook to create state | Returns [value, setterFunction] |
| setState | Function to update state | setCount(5) triggers re-render |
| Re-render | React runs component again with new state | UI updates automatically |
| Props | External data (read-only) | { name, age } from parent |
| State | Internal data (read + write) | Created inside component |
The State Pattern:
import { useState } from 'react';
function Component() {
// 1. Create state
const [value, setValue] = useState(initialValue);
// 2. Use state in JSX
return <div>{value}</div>;
// 3. Update state (triggers re-render)
setValue(newValue);
}
Golden Rules:
- State is for changing data - Use it for anything that changes over time
- Never mutate state directly - Always use the setter function
- State triggers re-renders - When state changes, React updates the UI
- useState only runs once - The initial value is only used on first render
- Multiple states are OK - Use multiple useState calls for unrelated data
One Sentence Summary: > "State is React's way of giving components memory - use useState to create a piece of state that persists between renders, update it with the setter function to trigger a re-render, and React will automatically keep your UI in sync with that data!"