Episode 2 — React Frontend Architecture NextJS / 2.6 — Component Architecture Principles
2.6 — Exercise Questions: Component Architecture Principles
How to use this material:
- Try answering each question before checking any reference
- Write actual code for hands-on questions — don't just think about it
- If you get stuck, revisit the relevant sub-topic file
- Mark questions you struggled with and revisit them in 48 hours
- Discuss tricky questions with peers for deeper understanding
2.6.a — Single Responsibility Principle (Q1–Q12)
Q1. Define the Single Responsibility Principle in your own words. What does "one reason to change" mean in the context of a React component?
Q2. List 5 code smells that indicate a component violates SRP.
Q3. You have this component. Identify how many responsibilities it has and list them:
function UserDashboard() {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
const [theme, setTheme] = useState('light');
useEffect(() => { fetch('/api/user').then(r => r.json()).then(setUser); }, []);
useEffect(() => { fetch('/api/posts').then(r => r.json()).then(setPosts); }, []);
useEffect(() => { document.body.className = theme; }, [theme]);
return (
<div>
<h1>{user?.name}</h1>
<button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>Toggle Theme</button>
<ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>
</div>
);
}
Q4. Refactor the component from Q3 by splitting it into components and hooks that each follow SRP.
Q5. When is a 200-line component acceptable and NOT a violation of SRP? Give an example.
Q6. Explain why separating data fetching into custom hooks is an application of SRP. What does each "layer" become responsible for?
Q7. A teammate argues: "SRP means every component should only render one HTML element." Is this correct? Explain with examples.
Q8. Write a utility function that extracts business logic from this component:
function Invoice({ items, taxRate, couponCode }) {
const subtotal = items.reduce((s, i) => s + i.price * i.qty, 0);
const discount = couponCode === 'SAVE20' ? subtotal * 0.2 : 0;
const tax = (subtotal - discount) * taxRate;
const total = subtotal - discount + tax;
return <div>Total: ${total.toFixed(2)}</div>;
}
Q9. What is the difference between SRP for components, hooks, and utility functions? Give one example of each.
Q10. Explain the "and" test for SRP. Apply it to a component called ProductListWithFiltersAndSorting.
Q11. You have a ChatRoom component that handles: message display, message input, typing indicators, online users, and emoji picker. Draw a component architecture diagram that follows SRP.
Q12. What's the risk of over-splitting? At what point does SRP harm more than help?
2.6.b — Smart vs Dumb Components (Q13–Q24)
Q13. Define "presentational component" and "container component." Give two examples of each.
Q14. Which of these state variables belong in a presentational component vs a container/hook?
isDropdownOpenusers(from API)activeTabformData(for submission)tooltipPosition
Q15. Why did Dan Abramov say the pattern is "less necessary" with hooks? What changed?
Q16. A design system Button component fetches its own icon from an icon API. Is this appropriate? Why or why not?
Q17. Write a presentational NotificationCard component that receives all data via props. Then write a container NotificationCardContainer that uses useNotification hook to provide the data.
Q18. Why are presentational components easier to test than container components? Show a test for each.
Q19. How does Storybook benefit from the smart/dumb separation? Can you use Storybook with container components? Why or why not?
Q20. Convert this mixed component into hook + presentational:
function TodoList() {
const [todos, setTodos] = useState([]);
useEffect(() => { fetch('/api/todos').then(r => r.json()).then(setTodos); }, []);
const toggle = (id) => setTodos(ts => ts.map(t => t.id === id ? {...t, done: !t.done} : t));
return <ul>{todos.map(t => <li key={t.id} onClick={() => toggle(t.id)}>{t.text}</li>)}</ul>;
}
Q21. In Next.js App Router, how do Server Components and Client Components map to the smart/dumb pattern?
Q22. When is it acceptable for a component to be BOTH smart and dumb (mix data fetching and rendering)?
Q23. Explain the "same UI, multiple data sources" scenario. Why does smart/dumb separation shine here?
Q24. What's the difference between a container component and a custom hook that replaces it? When do you still need a container component?
2.6.c — Lifting State Up (Q25–Q36)
Q25. Explain lifting state up in one paragraph. Use a concrete analogy.
Q26. Two components need to share a count value. Write the before (broken) and after (fixed) code using lifting state up.
Q27. What are "derived values"? Why should you derive instead of duplicating state? Give an example where duplicating leads to bugs.
Q28. In a temperature converter (Celsius ↔ Fahrenheit), why does the parent need to store the temperature AND which scale was last edited?
Q29. A search page has SearchBar, FilterPanel, and ResultsList. All need access to the search parameters. Show where the state should live and the data flow.
Q30. Explain why lifting state causes performance concerns. What are three solutions?
Q31. When should you NOT lift state up? Give three examples of state that should stay local.
Q32. A blog editor has EditorForm and LivePreview that need the same formData. Write the parent component that coordinates them.
Q33. What's the difference between lifting state up and using Context? When is each appropriate?
Q34. Explain the "single source of truth" principle. What bugs occur when you have two sources of truth?
Q35. Refactor this code to use derived state instead of synced state:
function Cart() {
const [items, setItems] = useState([]);
const [totalPrice, setTotalPrice] = useState(0);
const addItem = (item) => {
setItems(prev => [...prev, item]);
setTotalPrice(prev => prev + item.price);
};
}
Q36. Draw a component tree with App, Layout, Sidebar, Header, SearchBar, and ContentArea. If SearchBar and ContentArea need the same query, where should the state live? What if it's 5 levels deep?
2.6.d — Prop Drilling Problem (Q37–Q48)
Q37. Define prop drilling. What's the "prop drilling score" and how do you calculate it?
Q38. List 5 specific problems that prop drilling causes in a real application.
Q39. You have App → Layout → Header → NavBar → UserMenu where only UserMenu uses the user prop. What is the drilling score? Propose two solutions.
Q40. Implement a Context-based solution for the user authentication drilling problem from Q39.
Q41. Explain component composition (children/slots) as a solution to prop drilling. Show before and after code.
Q42. When is prop drilling actually fine and NOT worth solving? Give three examples.
Q43. Compare Context API vs Zustand for solving prop drilling. When would you choose each?
Q44. What is "URL state"? How does useSearchParams help avoid prop drilling for search/filter state?
Q45. Explain the "Provider hell" anti-pattern. How do you solve it when you have 7+ Context providers?
Q46. A teammate uses {...props} spread to "solve" prop drilling. Explain why this is an anti-pattern.
Q47. You're building an e-commerce site where the cart count needs to appear in the header, cart page, and checkout page (all different routes, deeply nested). What's the best solution and why?
Q48. What's the performance difference between prop drilling, Context, and Zustand when state updates frequently?
2.6.e — Component Composition (Q49–Q60)
Q49. Explain composition vs inheritance in React. Why does React prefer composition?
Q50. Write a Card component that uses the children prop. Then write a specialized AlertCard component that composes Card with pre-filled styles and an icon.
Q51. Implement a named slot pattern: a Dialog component with separate title, children (body), and footer slots.
Q52. What is the "compound component" pattern? Build a simple <Tabs> + <Tab> + <TabPanel> compound component.
Q53. Explain the render props pattern. When is it still useful in modern React vs custom hooks?
Q54. Show how composition eliminates prop drilling: given App → Layout → Header → UserMenu where only UserMenu needs user, use slot composition to pass user directly.
Q55. Build a layout system with Stack, Cluster, and Center components using composition.
Q56. What does React.Children.map do? When would you use cloneElement? Why are they considered "escape hatches"?
Q57. Compare these real-world APIs and identify which composition pattern each uses:
<Dialog.Root>/<Dialog.Content>(Radix)<Route path="/" element={<Home />}>(React Router)<CardHeader>/<CardContent>(shadcn/ui)
Q58. What is the polymorphic as prop pattern? Write a Box component that renders as any HTML element.
Q59. When does composition go wrong? Describe "wrapper hell" and show how to solve it.
Q60. Design a component API for a DataTable that supports custom columns, sorting, filtering, and pagination using compound components. Write the usage code (consumer-side API), not the implementation.