▶️ Live demo
Try it yourself — interact with the example below.
Loading demo…
🎯 What Is State Management?
Imagine you're organizing a library with thousands of books:
WITHOUT STATE MANAGEMENT (Chaos):
┌─────────────────────────────────────────┐
│ 📚 LIBRARY WITH NO CATALOG SYSTEM │
│ │
│ Books scattered everywhere! │
│ • "React Guide" on the floor │
│ • "JS Basics" in the kitchen │
│ • "CSS Mastery" under the couch │
│ │
│ You need "React Guide": │
│ "Where is it?!" 🤯 │
│ Search every room... │
│ Can't find it! │
│ │
│ In React: │
│ const [cart, setCart] = useState([]) │
│ // Is this in App? Header? Cart? │
│ // No one knows where state lives! │
│ │
│ Problems: │
│ • State in wrong components │
│ • Passed through components that │
│ don't need it (prop drilling) │
│ • Siblings can't share data │
│ • Bugs when state is out of sync! │
└─────────────────────────────────────────┘
WITH STATE MANAGEMENT (Organized):
┌─────────────────────────────────────────┐
│ 📚 LIBRARY WITH PERFECT CATALOG │
│ │
│ Every book has a HOME: │
│ • "React Guide" → React Shelf │
│ • "JS Basics" → JavaScript Shelf │
│ • "CSS Mastery" → Styling Shelf │
│ │
│ You need "React Guide": │
│ "Aisle 3, Shelf 2!" Easy! 😊 │
│ │
│ In React: │
│ Search state → SearchBar component │
│ Cart state → App component (shared) │
│ User state → App component (shared) │
│ Theme state → Header component (local) │
│ │
│ Benefits: │
│ • Every state has a home │
│ • Easy to find and update │
│ • Components get only what they need │
│ • No wasted re-renders! │
└─────────────────────────────────────────┘
THE STATE MANAGEMENT DECISION FLOWCHART:
┌─────────────────────────────────────────┐
│ Need to store data? │
│ ↓ │
│ Will it change? │
│ → NO → Use const variable │
│ → YES → Can it be derived? │
│ → YES → Calculate it! │
│ → NO → Should it re-render? │
│ → NO → Use useRef (later!) │
│ → YES → Create useState! │
│ → Only here? → Keep local! │
│ → Child needs? → Pass props! │
│ → Sibling needs? → Lift up! │
│ → Everyone? → Global! │
└─────────────────────────────────────────┘
⚠️ The Big Problem: "Where Should I Put This State?"
// ==========================================
// THE "STATE SCATTERED EVERYWHERE" TRAP
// ==========================================
// ❌ WRONG: All state dumped in App, bloated and confused
function BadApp() {
// Is this the right place for ALL this state?
const [cart, setCart] = useState([]); // Who needs this?
const [search, setSearch] = useState(""); // Only SearchBar?
const [user, setUser] = useState(null); // Everyone needs this?
const [theme, setTheme] = useState("light"); // Only Header?
const [menuOpen, setMenuOpen] = useState(false); // Only Header?
// Confusion: Which component should own what?
// Prop drilling: passing data through components
// that don't need it, just to reach children!
return (
<div>
<Header
user={user}
theme={theme}
setTheme={setTheme}
menuOpen={menuOpen}
setMenuOpen={setMenuOpen}
/>
<SearchBar search={search} setSearch={setSearch} />
<ProductList cart={cart} />
<Cart cart={cart} setCart={setCart} />
</div>
);
}
// Problems:
// 1. App component is bloated with state it doesn't use
// 2. Search state passed to App, but only SearchBar needs it
// 3. Menu state passed through App, but only Header needs it
// 4. Hard to track which component changes what
// 5. Unnecessary re-renders when unrelated state changes
// 6. Siblings like ProductList and Cart can't talk directly!
// ==========================================
// THE SOLUTION: Give Each State a Proper Home
// ==========================================
// ✅ CORRECT: Each state lives in the RIGHT component
function GoodApp() {
// 🏠 HOME: App (common parent of siblings)
// Needed by: Header(badge), ProductList, Cart, Checkout
const [cart, setCart] = useState([]);
// 🏠 HOME: App (shared across app)
// Needed by: Header, Profile, Orders, everywhere
const [user, setUser] = useState(null);
return (
<div>
{/* Header has its OWN menu/theme state inside it! */}
<Header user={user} cartCount={cart.length} />
{/* SearchBar has its OWN search state inside it! */}
<SearchBar />
{/* Siblings share cart via common parent (App) */}
<ProductList cart={cart} setCart={setCart} />
<Cart cart={cart} setCart={setCart} />
</div>
);
}
function SearchBar() {
// 🏠 HOME: SearchBar (only SearchBar needs this!)
const [search, setSearch] = useState("");
return (
<input
value={search}
onChange={e => setSearch(e.target.value)}
placeholder="Search products..."
/>
);
}
function Header({ user, cartCount }) {
// 🏠 HOME: Header (only Header needs these!)
const [theme, setTheme] = useState("light");
const [menuOpen, setMenuOpen] = useState(false);
return (
<header>
<button onClick={() => setMenuOpen(!menuOpen)}>Menu</button>
<span>🛒 {cartCount}</span>
<span>{user?.name}</span>
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
{theme}
</button>
</header>
);
}
// What happens when user searches for "Socks":
//
// 1. User types "S" in SearchBar
// → SearchBar's onChange fires
// → setSearch("S") updates LOCAL state
// → ONLY SearchBar re-renders! ✓
// → App does NOT re-render! ✓
// → Other components stay idle! ✓
//
// 2. User clicks "Add to Cart" on ProductList
// → ProductList calls setCart([...cart, product])
// → App's cart state updates
// → App re-renders
// → ProductList AND Cart BOTH re-render
// → Cart shows new item! ✓
// → Header shows updated count! ✓
//
// 3. Each piece of state lives in its proper home!
// → No wasted re-renders! ✓
// → Easy to find and debug! ✓
📋 Complete Visual Examples
Create file: react-state-management.js
// ==========================================
// STATE MANAGEMENT - Complete Guide
// ==========================================
/*
THE STATE DECISION FLOWCHART:
┌─────────────────────────────────────────┐
│ Need to store data? │
│ ↓ │
│ Will it change over time? │
│ → NO → Use a regular const variable │
│ → YES → Can it be derived from │
│ existing state or props? │
│ → YES → Calculate it! (Derived) │
│ → NO → Should updating trigger │
│ a re-render? │
│ → NO → Use useRef (later topic) │
│ → YES → Create useState! │
│ → Only this component needs it?│
│ → YES → Keep it LOCAL! │
│ → NO → Child needs it? │
│ → YES → Pass via PROPS! │
│ → NO → Sibling needs it? │
│ → YES → LIFT STATE UP! │
│ → NO → Many components? │
│ → YES → GLOBAL state! │
└─────────────────────────────────────────┘
LOCAL vs GLOBAL STATE:
┌─────────────────────────────────────────┐
│ LOCAL STATE (Private Bedroom) │
│ • Only one component + its children │
│ • Created with useState() │
│ • Example: Search input text │
│ │
│ GLOBAL STATE (Town Square) │
│ • Many components need access │
│ • Created with Context API or Redux │
│ • Example: Shopping cart, user login │
└─────────────────────────────────────────┘
*/
// ==========================================
// EXAMPLE 1: Local State (Private Bedroom)
// ==========================================
import { useState } from 'react';
function SearchBar() {
// 🏠 HOME: SearchBar owns this state
// Only SearchBar needs to know the search text
const [query, setQuery] = useState("");
return (
<div className="search-bar">
<input
type="text"
placeholder="Search courses..."
value={query} // ← Connected to state
onChange={e => setQuery(e.target.value)} // ← Updates state
/>
{/* Only THIS component displays the query */}
<p>Searching for: "{query}"</p>
</div>
);
}
// Visual Flow:
// Initial: query = ""
// input shows: "" (empty)
// <p>Searching for: ""</p>
//
// User types "React":
// onChange → e.target.value = "R"
// setQuery("R")
// React re-renders SearchBar ONLY
// query = "R"
// input shows: "R"
// <p>Searching for: "R"</p>
//
// Why this is efficient:
// → Only SearchBar re-renders!
// → App component does NOT re-render!
// → ProductList, Cart, Header stay idle!
// → Local state = Local updates! ✓
// ==========================================
// EXAMPLE 2: Parent → Child via Props (Passing the Key)
// ==========================================
function Counter() {
// 🏠 HOME: Counter owns the count
const [count, setCount] = useState(0); // ← State lives here
return (
<div className="counter">
{/* Parent displays count */}
<h2>Count: {count}</h2>
{/* Child receives count via props */}
<CounterDisplay count={count} /> // ← Pass data down
{/* Child receives updater via props */}
<CounterControls
count={count}
setCount={setCount}
/> // ← Pass updater down
</div>
);
}
// CHILD COMPONENT: Only DISPLAYS the count
// It receives the value but doesn't own it!
function CounterDisplay({ count }) { // ← Receives via props
// No useState here! Just displays what parent gave
return <p>Current value is: {count}</p>;
}
// CHILD COMPONENT: Can UPDATE the count
// It receives the setter and calls it!
function CounterControls({ count, setCount }) { // ← Receives both
return (
<div className="controls">
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(count - 1)}>-1</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
// Visual Flow:
// 1. Counter owns count = 0
// 2. CounterDisplay receives count={0} via props
// 3. CounterControls receives count={0} and setCount via props
// 4. User clicks +1 button in CounterControls
// 5. CounterControls calls setCount(1)
// 6. Counter's state updates to 1
// 7. Counter re-renders
// 8. Both children receive new count={1}
// 9. Both children re-render with updated value
//
// Key insight: Children don't own the state!
// They just BORROW it from parent via props!
// ==========================================
// EXAMPLE 3: Lifting State Up (The Sibling Problem)
// ==========================================
// THE PROBLEM: Two siblings need the SAME data
// ProductList needs to ADD to cart
// ShoppingCart needs to DISPLAY cart
// But siblings CANNOT pass props to each other!
// ❌ WRONG: State in wrong place (two separate carts!)
function WrongApp() {
return (
<div>
<ProductList /> {/* Has its own cart state? */}
<ShoppingCart /> {/* Has its own cart state? */}
{/* Two different carts! They don't sync! */}
{/* ProductList: "I added an item!" */}
{/* ShoppingCart: "I don't see it!" */}
</div>
);
}
// ✅ CORRECT: Lift state to common parent
function CorrectApp() {
// 🏠 HOME: App (the common parent of both siblings)
// This is the FIRST component that contains BOTH
// ProductList and ShoppingCart in its tree!
const [cart, setCart] = useState([]); // ← Lifted up here!
function handleAddToCart(product) {
setCart(prev => [...prev, product]); // ← Add item
}
function handleRemoveFromCart(productId) {
setCart(prev => prev.filter(item => item.id !== productId)); // ← Remove
}
return (
<div className="app">
<h1>🛒 My Shop</h1>
{/* Pass cart data AND updater to children */}
<ProductList
cart={cart} // ← Read access
onAddToCart={handleAddToCart} // ← Write access
/>
<ShoppingCart
cart={cart} // ← Read access
onRemoveFromCart={handleRemoveFromCart} // ← Write access
/>
</div>
);
}
// ProductList: Can ADD items (calls parent's function)
function ProductList({ cart, onAddToCart }) {
const products = [
{ id: 1, name: "React Course", price: 29 },
{ id: 2, name: "JavaScript Course", price: 19 },
{ id: 3, name: "CSS Course", price: 15 }
];
return (
<div className="product-list">
<h2>Products</h2>
{products.map(product => (
<div key={product.id} className="product">
<span>{product.name} - ${product.price}</span>
<button onClick={() => onAddToCart(product)}>
Add to Cart
</button>
</div>
))}
</div>
);
}
// ShoppingCart: Can VIEW and REMOVE items
function ShoppingCart({ cart, onRemoveFromCart }) {
// DERIVED STATE: Calculate total from cart!
const total = cart.reduce((sum, item) => sum + item.price, 0);
return (
<div className="cart">
<h2>Shopping Cart ({cart.length} items)</h2>
{cart.length === 0 ? (
<p>Your cart is empty 🛒</p>
) : (
<ul>
{cart.map(item => (
<li key={item.id}>
{item.name} - ${item.price}
<button onClick={() => onRemoveFromCart(item.id)}>
Remove
</button>
</li>
))}
</ul>
)}
<h3>Total: ${total}</h3>
</div>
);
}
// Visual Flow of Lifting State Up:
// 1. App owns cart = []
// 2. ProductList shows products, can call onAddToCart
// 3. ShoppingCart shows cart items, can call onRemoveFromCart
// 4. User clicks "Add to Cart" on React Course
// 5. ProductList calls onAddToCart(product)
// 6. App's handleAddToCart runs → setCart([...cart, product])
// 7. App's state updates to [{React Course}]
// 8. App re-renders
// 9. BOTH ProductList and ShoppingCart re-render
// 10. ShoppingCart now shows "React Course"! ✓
// 11. Both siblings are in sync! ✓
//
// Why this works:
// → Parent owns the "single source of truth"
// → Both children read from the SAME state
// → Updates flow through parent, keeping everyone in sync!
// ==========================================
// EXAMPLE 4: Derived State (Don't Store What You Can Calculate!)
// ==========================================
function Stats({ items }) {
// ❌ WRONG: Creating unnecessary state
// const [total, setTotal] = useState(0);
// const [count, setCount] = useState(0);
// const [average, setAverage] = useState(0);
//
// Every time items change, you must remember to update ALL three!
// useEffect(() => {
// setCount(items.length);
// setTotal(items.reduce((s, i) => s + i.price, 0));
// setAverage(items.length > 0 ? total / count : 0);
// }, [items]);
// Easy to forget one → BUGS! 🐛
// ✅ CORRECT: Derive from existing props/state
// These are NOT state! They are computed values!
const count = items.length; // ← Derived!
const total = items.reduce((sum, item) => sum + item.price, 0); // ← Derived!
const average = count > 0 ? total / count : 0; // ← Derived!
const isEmpty = count === 0; // ← Derived!
const mostExpensive = count > 0
? items.reduce((max, item) => item.price > max.price ? item : max)
: null; // ← Derived!
// React recalculates these EVERY render
// They are ALWAYS up-to-date! No useEffect needed! ✓
return (
<div className="stats">
<h2>🛒 Cart Statistics</h2>
<div className="stat-grid">
<div className="stat">
<span className="label">Items</span>
<span className="value">{count}</span>
</div>
<div className="stat">
<span className="label">Total</span>
<span className="value">${total.toFixed(2)}</span>
</div>
<div className="stat">
<span className="label">Average</span>
<span className="value">${average.toFixed(2)}</span>
</div>
</div>
{isEmpty && <p>Your cart is empty! Add some items!</p>}
{mostExpensive && (
<p>Most expensive: {mostExpensive.name} (${mostExpensive.price})</p>
)}
</div>
);
}
// ⚠️ IMPORTANT: Derived State Rules:
//
// If you can calculate it from props or existing state,
// DON'T store it in useState!
//
// ❌ WRONG:
// const [count, setCount] = useState(items.length);
//
// ✅ CORRECT:
// const count = items.length;
//
// Why? Because if items changes, count automatically updates!
// No manual syncing needed! No bugs! ✓
// ==========================================
// EXAMPLE 5: The Decision Flowchart in Code
// ==========================================
function DecisionFlowchartDemo() {
// Question 1: Do we need to store data?
// → YES! We need a theme for the app
// Question 2: Will it change?
// → YES! User can toggle between light and dark
const [theme, setTheme] = useState("light"); // ← STATE! ✓
// Question 3: Can it be derived from existing state?
// → YES! isDarkMode is just a calculation from theme
const isDarkMode = theme === "dark"; // ← DERIVED! No useState! ✓
// Question 4: Should updating re-render the component?
// → YES! UI must change colors when theme changes
// → So we use useState (not useRef)
// Question 5: Where should this live?
// → Many components need theme (Header, Main, Footer)
// → So it lives in App (common parent) and passes via props
// → (Later we'll learn Context API for truly global!)
// Question 6: What about a form in MainContent?
// → Only MainContent needs it → LOCAL STATE! ✓
const [email, setEmail] = useState(""); // ← Local to MainContent
// Question 7: What about a computed discount?
// → Derived from cart total → DERIVED STATE! ✓
const discount = cartTotal > 100 ? 10 : 0; // ← Calculated, not stored!
return (
<div className={`app ${isDarkMode ? "dark" : "light"}`}>
<Header theme={theme} setTheme={setTheme} />
<MainContent theme={theme} />
<Footer theme={theme} />
</div>
);
}
function Header({ theme, setTheme }) {
// Local state: only Header needs to know if menu is open
const [menuOpen, setMenuOpen] = useState(false); // ← LOCAL! ✓
return (
<header>
<button onClick={() => setMenuOpen(!menuOpen)}>Menu</button>
{menuOpen && <nav>...</nav>}
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
{theme === "light" ? "🌙 Dark" : "☀️ Light"}
</button>
</header>
);
}
// ==========================================
// EXAMPLE 6: Complete App with All Patterns
// ==========================================
const PRODUCTS = [
{ id: 1, name: "React Mastery", price: 89 },
{ id: 2, name: "JavaScript Pro", price: 69 },
{ id: 3, name: "CSS Wizard", price: 49 },
];
function CompleteApp() {
// GLOBAL-ish state: Shared by many components
// 🏠 HOME: App (common parent)
const [cart, setCart] = useState([]);
const [user, setUser] = useState({ name: "Alex" });
// Derived state: Calculate from cart
const cartTotal = cart.reduce((sum, item) => sum + item.price, 0);
const itemCount = cart.length;
const freeShipping = cartTotal > 100; // ← Derived! ✓
function handleAddToCart(product) {
setCart(prev => [...prev, product]);
}
function handleRemoveFromCart(productId) {
setCart(prev => prev.filter(item => item.id !== productId));
}
return (
<div className="complete-app">
{/* Header receives user and cart count */}
<Header user={user} cartCount={itemCount} />
{/* SearchBar has its OWN local state */}
<SearchBar />
{/* ProductList can add items */}
<ProductList
products={PRODUCTS}
onAddToCart={handleAddToCart}
/>
{/* ShoppingCart displays cart + derived stats */}
<ShoppingCart
cart={cart}
cartTotal={cartTotal}
freeShipping={freeShipping}
onRemoveFromCart={handleRemoveFromCart}
/>
</div>
);
}
🚀 Interactive React Usage Examples
Complete React File: StateManagementMasterClass.jsx
import React, { useState } from 'react';
// ==========================================
// INTERACTIVE STATE MANAGEMENT DEMO
// ==========================================
function StateManagementMasterClass() {
const [activeDemo, setActiveDemo] = useState('flowchart');
const demos = {
flowchart: { title: 'Decision Flowchart', component: <FlowchartDemo /> },
local: { title: 'Local State', component: <LocalStateDemo /> },
lifted: { title: 'Lift State Up', component: <LiftedStateDemo /> },
derived: { title: 'Derived State', component: <DerivedStateDemo /> },
broken: { title: 'Broken (Wrong Home)', component: <BrokenStateDemo /> }
};
return (
<div style={{ maxWidth: '900px', margin: '0 auto', padding: '20px', fontFamily: 'Arial, sans-serif' }}>
<header style={{ background: '#264653', color: 'white', padding: '30px', borderRadius: '10px', marginBottom: '30px' }}>
<h1 style={{ margin: 0 }}>🧠 State Management</h1>
<p style={{ margin: '10px 0 0 0', opacity: 0.9 }}>Giving Every Piece of State a Home</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 ? '#e76f51' : '#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: The Decision Flowchart
// ==========================================
function FlowchartDemo() {
const [step, setStep] = useState(1);
const steps = [
{
num: 1,
title: 'Need Data?',
question: 'Do you need to store some data?',
code: '// Any data that changes?',
visual: '📦',
desc: 'If no data to track, no state needed!'
},
{
num: 2,
title: 'Will It Change?',
question: 'Will this data change over time?',
code: 'const data = "fixed"; // No change? Use const!',
visual: '🔄',
desc: 'If it never changes, use a regular variable!'
},
{
num: 3,
title: 'Can It Be Derived?',
question: 'Can you calculate it from existing state/props?',
code: 'const total = items.reduce((s, i) => s + i.price, 0);',
visual: '🧮',
desc: 'If yes, DERIVE it! Don\'t store it!'
},
{
num: 4,
title: 'Need Re-render?',
question: 'Should updating trigger a re-render?',
code: 'const [count, setCount] = useState(0); // Re-renders!',
visual: '⚡',
desc: 'If yes, useState! If no, useRef (later)!'
},
{
num: 5,
title: 'Where To Place?',
question: 'Who needs this state?',
code: '// Only here? → Local\n// Child? → Props\n// Sibling? → Lift Up!',
visual: '🏠',
desc: 'Give it a home in the right component!'
}
];
const current = steps[step - 1];
return (
<div>
<h2>The State Decision Flowchart 🧭</h2>
<div style={{ display: 'flex', gap: '10px', marginBottom: '20px', justifyContent: 'center' }}>
{steps.map(s => (
<button
key={s.num}
onClick={() => setStep(s.num)}
style={{
width: '60px',
height: '60px',
borderRadius: '50%',
border: 'none',
cursor: 'pointer',
background: step >= s.num ? '#e76f51' : '#e0e0e0',
color: step >= s.num ? 'white' : '#333',
fontSize: '24px',
fontWeight: 'bold'
}}
>
{s.num}
</button>
))}
</div>
<div style={{ padding: '30px', background: 'white', borderRadius: '10px', border: '2px solid #264653' }}>
<h3 style={{ color: '#264653', marginTop: 0 }}>Step {current.num}: {current.title}</h3>
<p style={{ fontSize: '20px', color: '#555', fontWeight: 'bold' }}>{current.question}</p>
<div style={{ display: 'grid', gap: '20px', gridTemplateColumns: '1fr 1fr', marginBottom: '20px' }}>
<div>
<pre style={{
background: '#1e1e1e',
color: '#d4d4d4',
padding: '20px',
borderRadius: '8px',
fontSize: '16px'
}}>
{current.code}
</pre>
<p style={{ fontSize: '18px', color: '#555' }}>{current.desc}</p>
</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '80px' }}>
{current.visual}
</div>
</div>
</div>
<div style={{ marginTop: '20px', padding: '15px', background: '#e8f5e9', borderRadius: '8px' }}>
<h4>🧠 Memory Trick:</h4>
<p>"State is like a pet — keep it in the room where it belongs, don't let it wander the whole house!"</p>
</div>
</div>
);
}
// ==========================================
// DEMO 2: Local State
// ==========================================
function LocalStateDemo() {
const [search, setSearch] = useState("");
const [showExplanation, setShowExplanation] = useState(false);
return (
<div>
<h2>Local State (Private Bedroom) 🛏️</h2>
<div style={{ marginBottom: '20px', padding: '15px', background: '#e3f2fd', borderRadius: '8px' }}>
<h4>This state lives ONLY in this component! Other components don't know it exists!</h4>
</div>
<div style={{
padding: '40px',
background: 'white',
borderRadius: '10px',
textAlign: 'center',
boxShadow: '0 2px 10px rgba(0,0,0,0.1)'
}}>
<input
type="text"
placeholder="Search..."
value={search}
onChange={e => setSearch(e.target.value)}
style={{
padding: '15px',
fontSize: '18px',
width: '300px',
border: '2px solid #264653',
borderRadius: '8px'
}}
/>
<div style={{ marginTop: '30px', display: 'flex', gap: '20px', justifyContent: 'center' }}>
<div style={{ padding: '20px', background: '#e3f2fd', borderRadius: '8px', minWidth: '200px' }}>
<div style={{ fontSize: '12px', color: '#666' }}>Local State Value</div>
<div style={{ fontSize: '24px', fontWeight: 'bold', color: '#264653', fontFamily: 'monospace' }}>
"{search}"
</div>
</div>
<div style={{ padding: '20px', background: '#fff3e0', borderRadius: '8px', minWidth: '200px' }}>
<div style={{ fontSize: '12px', color: '#666' }}>Re-renders</div>
<div style={{ fontSize: '24px', fontWeight: 'bold', color: '#e76f51' }}>
Only This Component!
</div>
</div>
</div>
</div>
<div style={{ marginTop: '20px', textAlign: 'center' }}>
<button
onClick={() => setShowExplanation(!showExplanation)}
style={{ padding: '10px 20px', background: '#264653', color: 'white', border: 'none', borderRadius: '5px' }}
>
{showExplanation ? 'Hide' : 'Show'} Why Local Is Efficient
</button>
</div>
{showExplanation && (
<div style={{ marginTop: '20px', padding: '20px', background: '#fff3e0', borderRadius: '8px' }}>
<h4>🔍 Why Local State Is Great:</h4>
<ol style={{ lineHeight: '2' }}>
<li>User types in SearchBar</li>
<li>Only SearchBar's state changes</li>
<li>Only SearchBar re-renders</li>
<li>Parent (App) does NOT re-render</li>
<li>Siblings (Cart, ProductList) do NOT re-render</li>
<li>Efficient! No wasted work! ✨</li>
</ol>
</div>
)}
</div>
);
}
// ==========================================
// DEMO 3: Lifted State (Siblings)
// ==========================================
function LiftedStateDemo() {
const [cart, setCart] = useState([]);
const products = [
{ id: 1, name: "React Book", price: 30 },
{ id: 2, name: "JS Book", price: 25 },
];
function addToCart(product) {
setCart(prev => [...prev, product]);
}
function removeFromCart(productId) {
setCart(prev => prev.filter(item => item.id !== productId));
}
return (
<div>
<h2>Lifting State Up (Siblings) 🤝</h2>
<div style={{ marginBottom: '20px', padding: '15px', background: '#e3f2fd', borderRadius: '8px' }}>
<h4>Parent owns the state. Both children receive it via props. They stay in sync!</h4>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}>
{/* Product List */}
<div style={{ padding: '20px', background: 'white', borderRadius: '10px', boxShadow: '0 2px 10px rgba(0,0,0,0.1)' }}>
<h3 style={{ color: '#264653' }}>📦 Products</h3>
<p style={{ color: '#666', fontSize: '14px' }}>Can ADD items</p>
{products.map(p => (
<div key={p.id} style={{ display: 'flex', justifyContent: 'space-between', padding: '10px', borderBottom: '1px solid #eee' }}>
<span>{p.name} - ${p.price}</span>
<button
onClick={() => addToCart(p)}
style={{ padding: '5px 15px', background: '#2a9d8f', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
Add
</button>
</div>
))}
</div>
{/* Shopping Cart */}
<div style={{ padding: '20px', background: 'white', borderRadius: '10px', boxShadow: '0 2px 10px rgba(0,0,0,0.1)' }}>
<h3 style={{ color: '#264653' }}>🛒 Cart ({cart.length})</h3>
<p style={{ color: '#666', fontSize: '14px' }}>Can REMOVE items</p>
{cart.length === 0 ? (
<p style={{ color: '#999' }}>Empty...</p>
) : (
cart.map((item, i) => (
<div key={`${item.id}-${i}`} style={{ display: 'flex', justifyContent: 'space-between', padding: '10px', borderBottom: '1px solid #eee' }}>
<span>{item.name}</span>
<button
onClick={() => removeFromCart(item.id)}
style={{ padding: '5px 15px', background: '#e76f51', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
Remove
</button>
</div>
))
)}
<div style={{ marginTop: '10px', padding: '10px', background: '#f5f5f5', borderRadius: '5px', textAlign: 'center' }}>
<strong>Total: ${cart.reduce((s, i) => s + i.price, 0)}</strong>
</div>
</div>
</div>
<div style={{ marginTop: '20px', padding: '15px', background: '#e8f5e9', borderRadius: '8px' }}>
<h4>✅ How It Works:</h4>
<ol>
<li>Parent owns <code>cart</code> state</li>
<li>Products child receives <code>onAddToCart</code> function</li>
<li>Cart child receives <code>cart</code> array and <code>onRemoveFromCart</code></li>
<li>Both read from the SAME source of truth!</li>
<li>Always in sync! No duplicate data!</li>
</ol>
</div>
</div>
);
}
// ==========================================
// DEMO 4: Derived State
// ==========================================
function DerivedStateDemo() {
const [items, setItems] = useState([
{ id: 1, name: "Apple", price: 1.50 },
{ id: 2, name: "Banana", price: 0.75 },
{ id: 3, name: "Orange", price: 2.00 }
]);
const [showType, setShowType] = useState(false);
// DERIVED VALUES - Not state!
const count = items.length;
const total = items.reduce((sum, item) => sum + item.price, 0);
const average = count > 0 ? total / count : 0;
return (
<div>
<h2>Derived State (Calculate, Don't Store!) 🧮</h2>
<div style={{ marginBottom: '20px', padding: '15px', background: '#e3f2fd', borderRadius: '8px' }}>
<h4>These values are CALCULATED from the items array. They are NOT stored in useState!</h4>
</div>
<div style={{
padding: '40px',
background: 'white',
borderRadius: '10px',
textAlign: 'center',
boxShadow: '0 2px 10px rgba(0,0,0,0.1)'
}}>
<div style={{ display: 'flex', gap: '20px', justifyContent: 'center', marginBottom: '20px' }}>
<div style={{ padding: '20px', background: '#e8f5e9', borderRadius: '8px', minWidth: '150px' }}>
<div style={{ fontSize: '12px', color: '#666' }}>count</div>
<div style={{ fontSize: '24px', fontWeight: 'bold', color: '#2e7d32' }}>
{count}
</div>
<div style={{ fontSize: '12px', color: '#999' }}>items.length</div>
</div>
<div style={{ padding: '20px', background: '#e3f2fd', borderRadius: '8px', minWidth: '150px' }}>
<div style={{ fontSize: '12px', color: '#666' }}>total</div>
<div style={{ fontSize: '24px', fontWeight: 'bold', color: '#264653' }}>
${total.toFixed(2)}
</div>
<div style={{ fontSize: '12px', color: '#999' }}>reduce()</div>
</div>
<div style={{ padding: '20px', background: '#fff3e0', borderRadius: '8px', minWidth: '150px' }}>
<div style={{ fontSize: '12px', color: '#666' }}>average</div>
<div style={{ fontSize: '24px', fontWeight: 'bold', color: '#e76f51' }}>
${average.toFixed(2)}
</div>
<div style={{ fontSize: '12px', color: '#999' }}>total / count</div>
</div>
</div>
<button
onClick={() => setItems(prev => [...prev, { id: Date.now(), name: "Grape", price: 1.25 }])}
style={{ padding: '10px 20px', background: '#2a9d8f', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', marginRight: '10px' }}
>
Add Grape
</button>
<button
onClick={() => setItems(prev => prev.slice(0, -1))}
style={{ padding: '10px 20px', background: '#e76f51', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
Remove Last
</button>
</div>
<div style={{ marginTop: '20px', padding: '15px', background: '#fff3e0', borderRadius: '8px' }}>
<h4>⚠️ The Golden Rule:</h4>
<div style={{ fontFamily: 'monospace', fontSize: '14px', lineHeight: '2' }}>
<span style={{ color: '#c62828' }}>❌ Wrong (unnecessary state):</span><br/>
{`const [total, setTotal] = useState(0);`}<br/>
{'// Must manually update every time items change!'}<br/>
<br/>
<span style={{ color: '#2e7d32' }}>✅ Correct (derived):</span><br/>
{`const total = items.reduce((s, i) => s + i.price, 0);`}<br/>
{'// Automatically updates when items change! ✓'}
</div>
</div>
</div>
);
}
// ==========================================
// DEMO 5: Broken (State in Wrong Home)
// ==========================================
function BrokenStateDemo() {
return (
<div>
<h2>Broken State Placement ❌</h2>
<div style={{ marginBottom: '20px', padding: '15px', background: '#ffebee', borderRadius: '8px' }}>
<h4>These two counters don't sync because they each have their OWN state!</h4>
</div>
<BrokenCounter name="Counter A" />
<BrokenCounter name="Counter B" />
<div style={{ marginTop: '20px', padding: '15px', background: '#fff3e0', borderRadius: '8px' }}>
<h4>🧠 The Problem:</h4>
<ol>
<li>Each component has its own <code>useState(0)</code></li>
<li>They are INDEPENDENT pieces of state!</li>
<li>Clicking A doesn't change B!</li>
<li>They should share state via a common parent!</li>
</ol>
</div>
<div style={{ marginTop: '20px', padding: '20px', background: '#e8f5e9', borderRadius: '8px', textAlign: 'left' }}>
<h4 style={{ color: '#2e7d32' }}>✅ Fix: Lift State Up!</h4>
<pre style={{ background: '#1e1e1e', color: '#d4d4d4', padding: '15px', borderRadius: '4px' }}>
{`function Parent() {
const [count, setCount] = useState(0); // ← One source of truth!
return (
<>
<ChildA count={count} setCount={setCount} />
<ChildB count={count} setCount={setCount} />
</>
);
}`}
</pre>
</div>
</div>
);
}
function BrokenCounter({ name }) {
const [count, setCount] = useState(0); // ← Each has its own!
return (
<div style={{
padding: '20px',
background: 'white',
borderRadius: '10px',
textAlign: 'center',
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
marginBottom: '20px'
}}>
<h3>{name}</h3>
<div style={{ fontSize: '48px', fontWeight: 'bold', color: '#264653' }}>{count}</div>
<button
onClick={() => setCount(c => c + 1)}
style={{ padding: '10px 20px', background: '#e76f51', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
+1
</button>
</div>
);
}
export default StateManagementMasterClass;
🧠 Memory Aids for Poor Logic Thinking
The "House & Rooms" Analogy
┌─────────────────────────────────────────────────┐
│ STATE MANAGEMENT = ORGANIZING YOUR HOUSE │
│ │
│ Your App is a House: │
│ ┌─────────────────────────────────────────┐ │
│ │ 🏠 APP COMPONENT (The House) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Header │ │ Sidebar │ Rooms = │ │
│ │ │ (Room) │ │ (Room) │ Components │ │
│ │ └──────────┘ └──────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────┐ │ │
│ │ │ MainContent (Big Room) │ │ │
│ │ │ ┌──────────┐ │ │ │
│ │ │ │ Product │ Nested │ │ │
│ │ │ │ Card │ rooms │ │ │
│ │ │ └──────────┘ │ │ │
│ │ └──────────────────────────┘ │ │
│ └─────────────────────────────────────────┘ │
│ │
│ STATE = Items in your house: │
│ ┌─────────────────────────────────────────┐ │
│ │ 🗝️ Keys → Key Hook by the door │ │
│ │ (State lives in Header - local) │ │
│ │ │ │
│ │ 📺 TV Remote → Living room table │ │
│ │ (State lives in MainContent - local) │ │
│ │ │ │
│ │ 🍞 Family Groceries → Kitchen │ │
│ │ (Everyone eats - global/lifted) │ │
│ │ │ │
│ │ 💰 Family Budget → Parent's office │ │
│ │ (Shared - lifted to App) │ │
│ └─────────────────────────────────────────┘ │
│ │
│ LIFTING STATE UP = Moving an item to a room │
│ where EVERYONE who needs it can access it! │
│ │
│ Example: │
│ • You put cookies in your bedroom │
│ • Your sibling wants cookies too │
│ • Solution: Move cookies to KITCHEN │
│ • (Common area = Common parent component) │
└─────────────────────────────────────────────────┘
The "Mail Delivery" Analogy for Props
┌─────────────────────────────────────────────────┐
│ PROPS = PASSING MAIL BETWEEN ROOMS │
│ │
│ You (state) live in your bedroom. │
│ But your friend in another room needs info. │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ YOU (Component with state) │ │
│ │ "I know the secret number: 42" │ │
│ │ │ │
│ │ 📬 You write a letter (props): │ │
│ │ "The number is 42" │ │
│ │ │ │
│ │ 👤 You give it to the mailman (React) │ │
│ │ who delivers to your friend │ │
│ │ │ │
│ │ 📬 FRIEND (Child component) receives: │ │
│ │ props.number = 42 │ │
│ │ "Ah! The number is 42!" │ │
│ └─────────────────────────────────────────┘ │
│ │
│ IMPORTANT: Mail only goes DOWNSTAIRS! │
│ (Parent → Child) │
│ Children CANNOT send mail upstairs! │
│ (Child → Parent is NOT allowed directly!) │
│ │
│ That's why we LIFT STATE UP: │
│ Move the person (state) to a room where │
│ BOTH friends can receive the mail! │
└─────────────────────────────────────────────────┘
The "Elevator" Analogy for Lifting State Up
┌─────────────────────────────────────────────────┐
│ LIFTING STATE UP = MOVING THE ELEVATOR │
│ │
│ Imagine a building with floors: │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ 🏢 FLOOR 3: App Component │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Product │ │ Shopping │ │ │
│ │ │ List │ │ Cart │ │ │
│ │ │ (Floor 1)│ │ (Floor 2)│ │ │
│ │ └──────────┘ └──────────┘ │ │
│ │ ↑ ↑ │ │
│ │ └──────┬───────┘ │ │
│ │ │ │ │
│ │ ┌──────┴───────┐ │ │
│ │ │ ELEVATOR │ │ │
│ │ │ (State) │ │ │
│ │ └──────────────┘ │ │
│ └─────────────────────────────────────────┘ │
│ │
│ PROBLEM: Cart state is in Shopping Cart (Floor 2)│
│ Product List (Floor 1) CANNOT access it! │
│ They're on different floors! │
│ │
│ SOLUTION: Move the elevator to Floor 3 (App)! │
│ Now BOTH floors can access the cart! │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ 🏢 FLOOR 3: App (Now has cart state!) │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Product │ │ Shopping │ │ │
│ │ │ List │ │ Cart │ │ │
│ │ │ receives │ │ receives │ │ │
│ │ │ cart via │ │ cart via │ │ │
│ │ │ props! ✓ │ │ props! ✓ │ │ │
│ │ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────┘ │
│ │
│ The "Elevator" (state) moved UP to the common │
│ parent so ALL children can use it! │
└─────────────────────────────────────────────────┘
The "Photocopy" Analogy for Derived State
┌─────────────────────────────────────────────────┐
│ DERIVED STATE = MAKING PHOTOCOPIES │
│ │
│ You have ONE original document (state): │
│ ┌─────────────────────────────────────────┐ │
│ │ 📄 ORIGINAL: items array │ │
│ │ [Apple, Banana, Orange] │ │
│ │ This is the REAL state! │ │
│ └─────────────────────────────────────────┘ │
│ │
│ You need to know: │
│ • How many items? → Make a photocopy (derive) │
│ • Total price? → Make a photocopy (derive) │
│ • Is it empty? → Make a photocopy (derive) │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ 📠 PHOTOCOPIES (Derived values): │ │
│ │ │ │
│ │ Copy 1: itemCount = 3 │ │
│ │ Copy 2: totalPrice = $4.25 │ │
│ │ Copy 3: isEmpty = false │ │
│ │ │ │
│ │ These are NOT new documents! │ │
│ │ They are calculated FROM the original! │ │
│ │ │ │
│ │ If original changes: │ │
│ │ items = [Apple, Banana] │ │
│ │ → Copies automatically update! │ │
│ │ itemCount = 2, totalPrice = $2.25 │ │
│ │ │ │
│ │ ❌ WRONG: Create separate state for each │ │
│ │ ✅ CORRECT: Calculate from original │ │
│ └─────────────────────────────────────────┘ │
│ │
│ Memory Trick: │
│ "If you can calculate it, don't store it!" │
│ "Photocopies update when the original changes!"│
└─────────────────────────────────────────────────┘
🎓 Practice Exercises
Exercise 1: Identify the Right Home for State
Look at this component tree. Where should each state live?
App
├── Header
│ ├── Logo
│ └── UserMenu
├── Sidebar
│ └── Navigation
└── MainContent
├── ProductList
│ └── ProductCard (x10)
└── ShoppingCart
└── CartItem (x?)
State to place:
searchQuery- User's search textcart- Items in shopping cartisMenuOpen- Mobile menu open/closedtheme- Dark/light modeproducts- List of available products
Solution:
// ==========================================
// SOLUTION: Where Each State Lives
// ==========================================
function App() {
// 🏠 HOME: App (common parent)
// Needed by: Header, MainContent, Sidebar
const [theme, setTheme] = useState("light");
// 🏠 HOME: App (common parent - siblings need it)
// ProductList adds items, ShoppingCart displays them
const [cart, setCart] = useState([]);
// 🏠 HOME: App (data from API, used by ProductList)
const [products, setProducts] = useState([]);
return (
<div className={theme}>
<Header theme={theme} setTheme={setTheme} />
<Sidebar />
<MainContent
products={products}
cart={cart}
setCart={setCart}
/>
</div>
);
}
function Header({ theme, setTheme }) {
// 🏠 HOME: Header (only Header needs this)
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
<header>
<Logo />
<button onClick={() => setIsMenuOpen(!isMenuOpen)}>
Menu
</button>
{isMenuOpen && <UserMenu />}
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Toggle Theme
</button>
</header>
);
}
function MainContent({ products, cart, setCart }) {
// 🏠 HOME: MainContent (only MainContent needs this)
const [searchQuery, setSearchQuery] = useState("");
// Derived state!
const filteredProducts = products.filter(p =>
p.name.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
<main>
<input
value={searchQuery}
onChange={e => setSearchQuery(e.target.value)}
placeholder="Search products..."
/>
<ProductList
products={filteredProducts}
onAddToCart={product => setCart(prev => [...prev, product])}
/>
<ShoppingCart cart={cart} />
</main>
);
}
Exercise 2: Fix the State Placement Bug
This code has a bug. The counter in Display and the buttons in Controls don't sync. Fix it!
// ❌ BROKEN: State in wrong place
function App() {
return (
<div>
<CounterDisplay />
<CounterControls />
</div>
);
}
function CounterDisplay() {
const [count, setCount] = useState(0); // ❌ Wrong home!
return <p>Count: {count}</p>;
}
function CounterControls() {
const [count, setCount] = useState(0); // ❌ Wrong home! (Different state!)
return (
<div>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
);
}
Solution:
// ✅ FIXED: Lifted state to common parent
function App() {
// 🏠 HOME: App (common parent of Display and Controls)
const [count, setCount] = useState(0);
return (
<div>
<CounterDisplay count={count} />
<CounterControls count={count} setCount={setCount} />
</div>
);
}
function CounterDisplay({ count }) {
// No state here! Just receives props
return <p>Count: {count}</p>;
}
function CounterControls({ count, setCount }) {
return (
<div>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
Exercise 3: Convert to Derived State
This component stores unnecessary state. Fix it using derived state!
// ❌ WRONG: Unnecessary state
function Stats({ items }) {
const [total, setTotal] = useState(0);
const [count, setCount] = useState(0);
const [average, setAverage] = useState(0);
// ❌ Manual updates - easy to forget!
useEffect(() => {
setCount(items.length);
setTotal(items.reduce((s, i) => s + i.price, 0));
setAverage(items.length > 0 ? total / count : 0);
}, [items]);
return (
<div>
<p>Items: {count}</p>
<p>Total: ${total}</p>
<p>Average: ${average}</p>
</div>
);
}
Solution:
// ✅ CORRECT: Derived state
function Stats({ items }) {
// Calculate directly from props - no useState needed!
const count = items.length;
const total = items.reduce((sum, item) => sum + item.price, 0);
const average = count > 0 ? total / count : 0;
const isEmpty = count === 0;
return (
<div className="stats">
<p>Items: {count}</p>
<p>Total: ${total.toFixed(2)}</p>
<p>Average: ${average.toFixed(2)}</p>
{isEmpty && <p>No items yet!</p>}
</div>
);
}
Exercise 4: The State Decision Flowchart
You need to track whether a form has been submitted. Walk through the decision chart:
Questions:
- Will this data change? → Yes/No
- Can it be derived from existing state/props? → Yes/No
- Should updating re-render the component? → Yes/No
- Where should it live?
Solution:
function ContactForm() {
// 1. Does it change? → YES! (false → true when submitted)
// 2. Can it be derived? → NO! (independent data)
// 3. Should it re-render? → YES! (show success message)
// 4. Where? → ContactForm only needs it = LOCAL STATE! ✓
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [isSubmitted, setIsSubmitted] = useState(false); // ← NEW STATE!
function handleSubmit(e) {
e.preventDefault();
// Validation
if (!name || !email) return;
// Update state - triggers re-render to show success!
setIsSubmitted(true);
}
if (isSubmitted) {
return (
<div className="success">
<h2>✅ Thank you, {name}!</h2>
<p>We'll contact you at {email}</p>
</div>
);
}
return (
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={e => setName(e.target.value)}
placeholder="Name"
/>
<input
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Email"
/>
<button>Submit</button>
</form>
);
}
💡 Key Takeaways
| Concept | What It Means | Example |
|---|---|---|
| State Management | Giving each state a home in your code | Deciding which component owns useState |
| Local State | Only one component needs it | Search input in SearchBar |
| Global State | Many components need it | Shopping cart, user login |
| Lifting State Up | Move state to common parent | Cart moved from ProductList to App |
| Derived State | Calculate from existing state | total = items.reduce(...) |
| Props | Passing data parent → child | <Child data={state} /> |
| useState | Creates local state | const [x, setX] = useState(0) |
| Common Parent | First ancestor of all components that need state | App is parent of ProductList and Cart |
The State Decision Pattern:
function StateDecision() {
// Step 1: Need to store data?
// Step 2: Will it change? → YES
// Step 3: Can it be derived? → NO
// Step 4: Should it re-render? → YES
// Step 5: Create useState!
const [value, setValue] = useState("");
// Step 6: Only here? → Keep local!
// Step 7: Child needs it? → Pass via props!
// Step 8: Sibling needs it? → Lift to common parent!
// Step 9: Many components? → Global state (later!)
}
Golden Rules:
- Always start with local state — Only move up if needed!
- If you can derive it, don't store it — Calculate from existing state!
- Lift state to the closest common parent — Don't put everything in App!
- Props only flow down — Parent → Child, never reverse!
- One source of truth — Don't duplicate state in multiple components!
- State should be minimal — Store the least amount needed, derive the rest!
One Sentence Summary: > "State management in React is the practice of giving each piece of state a home by first asking whether the data changes and if it can be derived from existing state, then creating local state with useState in the component that needs it, passing it to children via props when necessary, and lifting state up to the closest common parent component when siblings both need access to the same data, while always preferring derived calculations over additional state and only moving to global state management when truly necessary!"