JavaScript Curriculum
Component Patterns
hardThe coffee shop UI needs protected routes (HOC), a tabbed menu (compound components), and a reusable rating widget (controlled component). Each pattern solves a different composition problem.
Component Patterns
These patterns are design solutions to recurring problems in React architecture. Knowing them helps you read any codebase and choose the right tool for each situation.
Four Patterns — Controlled, HOC, Render Props, Compound
When to use each and how they compare:
The parent fully controls the component's value via props. The component calls a callback to request changes — it owns no state of its own. The most common and recommended pattern in React.
// ✓ Controlled — parent owns value function RatingInput({ value, onChange }) { return ( <div className="rating"> {[1, 2, 3, 4, 5].map(star => ( <button key={star} onClick={() => onChange(star)} className={star <= value ? 'star filled' : 'star'} > ★ </button> ))} </div> ) } // Parent controls value: function ReviewForm() { const [rating, setRating] = useState(0) return <RatingInput value={rating} onChange={setRating} /> }
HOC vs Custom Hooks — The Modern Verdict
Why hooks replaced HOCs for logic sharing:
// ✓ Custom hooks approach — flat and composable function Dashboard() { const user = useAuth() // replaces withAuth const logging = useLogging() // replaces withLogging // ErrorBoundary still as component (can't hook that) if (!user) return <Navigate to="/login" /> return <div>{user.name}'s Dashboard</div> } // Hooks are just function calls — no nesting: function useAuth() { const user = useSelector(state => state.user) return user // null if not logged in } function useLogging() { const name = useRef(null) useEffect(() => { logRender(name.current) }, []) } // Advantages over HOC: // ✓ No wrapper nesting // ✓ Explicit — you can see all dependencies // ✓ Easy to test in isolation // ✓ No prop collisions // ✓ Works with TypeScript naturally
Compound Components — Live Demo
A working Tabs component built with the compound pattern:
Live compound Tabs component — Tabs, TabList, Tab, and TabPanel all share state via Context. No props passed between siblings.
A concentrated shot of coffee, rich and intense.
Your Challenge
Build Tabs with Context. Attach Tabs.List, Tabs.Tab, Tabs.Panel as properties. Tab should highlight when active === id. Panel should only render when active === id. Build a controlled StarRating that accepts value and onChange. Replace a hypothetical withAuth(Component) HOC with a useAuth() hook.
Challenge
Build a compound Tabs component with Tabs, Tabs.List, Tabs.Tab, Tabs.Panel. Use Context for shared state. Build a controlled StarRating component. Replace a legacy withAuth HOC with a useAuth custom hook.
Component Patterns
hardThe coffee shop UI needs protected routes (HOC), a tabbed menu (compound components), and a reusable rating widget (controlled component). Each pattern solves a different composition problem.
Component Patterns
These patterns are design solutions to recurring problems in React architecture. Knowing them helps you read any codebase and choose the right tool for each situation.
Four Patterns — Controlled, HOC, Render Props, Compound
When to use each and how they compare:
The parent fully controls the component's value via props. The component calls a callback to request changes — it owns no state of its own. The most common and recommended pattern in React.
// ✓ Controlled — parent owns value function RatingInput({ value, onChange }) { return ( <div className="rating"> {[1, 2, 3, 4, 5].map(star => ( <button key={star} onClick={() => onChange(star)} className={star <= value ? 'star filled' : 'star'} > ★ </button> ))} </div> ) } // Parent controls value: function ReviewForm() { const [rating, setRating] = useState(0) return <RatingInput value={rating} onChange={setRating} /> }
HOC vs Custom Hooks — The Modern Verdict
Why hooks replaced HOCs for logic sharing:
// ✓ Custom hooks approach — flat and composable function Dashboard() { const user = useAuth() // replaces withAuth const logging = useLogging() // replaces withLogging // ErrorBoundary still as component (can't hook that) if (!user) return <Navigate to="/login" /> return <div>{user.name}'s Dashboard</div> } // Hooks are just function calls — no nesting: function useAuth() { const user = useSelector(state => state.user) return user // null if not logged in } function useLogging() { const name = useRef(null) useEffect(() => { logRender(name.current) }, []) } // Advantages over HOC: // ✓ No wrapper nesting // ✓ Explicit — you can see all dependencies // ✓ Easy to test in isolation // ✓ No prop collisions // ✓ Works with TypeScript naturally
Compound Components — Live Demo
A working Tabs component built with the compound pattern:
Live compound Tabs component — Tabs, TabList, Tab, and TabPanel all share state via Context. No props passed between siblings.
A concentrated shot of coffee, rich and intense.
Your Challenge
Build Tabs with Context. Attach Tabs.List, Tabs.Tab, Tabs.Panel as properties. Tab should highlight when active === id. Panel should only render when active === id. Build a controlled StarRating that accepts value and onChange. Replace a hypothetical withAuth(Component) HOC with a useAuth() hook.
Challenge
Build a compound Tabs component with Tabs, Tabs.List, Tabs.Tab, Tabs.Panel. Use Context for shared state. Build a controlled StarRating component. Replace a legacy withAuth HOC with a useAuth custom hook.