Composition Patterns
Composition Patterns
React favors composition over inheritance.
Basic Composition
function App() {
return (
<Layout>
<Header />
<Sidebar />
<Content />
<Footer />
</Layout>
);
}
function Layout({ children }) {
return (
<div className="layout">
<header>Logo</header>
<main>{children}</main>
<footer>© 2024</footer>
</div>
);
}
Slot Pattern
function Card({ header, footer, children }) {
return (
<div className="card">
{header && <div className="card-header">{header}</div>}
<div className="card-body">{children}</div>
{footer && <div className="card-footer">{footer}</div>}
</div>
);
}
// Usage
<Card
header={<h2>Title</h2>}
footer={<button>Save</button>}
>
<p>Card content</p>
</Card>
Higher-Order Components (HOC)
// HOC that adds logging
function withLogging(WrappedComponent) {
return function WithLogging(props) {
useEffect(() => {
console.log(`${WrappedComponent.name} rendered`);
});
return <WrappedComponent {...props} />;
};
}
// Usage
const LoggedButton = withLogging(Button);
Compound Components
function Tabs({ children }) {
const [activeTab, setActiveTab] = useState(0);
return (
<div className="tabs">
{React.Children.map(children, (child, index) =>
React.cloneElement(child, {
isActive: index === activeTab,
onClick: () => setActiveTab(index)
})
)}
</div>
);
}
function Tab({ children, isActive, onClick }) {
return (
<button className={isActive ? "active" : ""} onClick={onClick}>
{children}
</button>
);
}
// Usage
<Tabs>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</Tabs>
Render Props
Render Props
A render prop is a function prop that returns JSX.
Basic Render Prop
function MouseTracker({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const handleMouseMove = (e) => {
setPosition({ x: e.clientX, y: e.clientY });
};
window.addEventListener("mousemove", handleMouseMove);
return () => window.removeEventListener("mousemove", handleMouseMove);
}, []);
return render(position);
}
// Usage
function App() {
return (
<MouseTracker
render={({ x, y }) => (
<p>Mouse is at ({x}, {y})</p>
)}
/>
);
}
Render Prop with Data
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [url]);
return render({ data, loading, error });
}
// Usage
<DataFetcher
url="/api/users"
render={({ data, loading, error }) => {
if (loading) return <Spinner />;
if (error) return <Error message={error} />;
return <UserList users={data} />;
}}
/>
Render Props vs Hooks
// Render Prop pattern (older)
function useMousePosition() {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const handleMouseMove = (e) => {
setPosition({ x: e.clientX, y: e.clientY });
};
window.addEventListener("mousemove", handleMouseMove);
return () => window.removeEventListener("mousemove", handleMouseMove);
}, []);
return position;
}
// Custom Hook (modern)
function MouseTracker() {
const position = useMousePosition();
return <p>Mouse at ({position.x}, {position.y})</p>;
}
Children as Function
function Toggle({ children }) {
const [on, setOn] = useState(false);
return children({
on,
toggle: () => setOn((prev) => !prev)
});
}
// Usage
<Toggle>
{({ on, toggle }) => (
<div>
<p>Toggle is {on ? "ON" : "OFF"}</p>
<button onClick={toggle}>Toggle</button>
</div>
)}
</Toggle>
Children Prop
Children Prop
Basic Children
function Card({ children }) {
return <div className="card">{children}</div>;
}
// Usage
<Card>
<h2>Title</h2>
<p>Content</p>
</Card>
Children with Additional Props
function Alert({ children, type = "info" }) {
return <div className={`alert alert-${type}`}>{children}</div>;
}
// Usage
<Alert type="error">Something went wrong!</Alert>
Multiple Children Slots
function Modal({ header, footer, children }) {
return (
<div className="modal">
<div className="modal-header">{header}</div>
<div className="modal-body">{children}</div>
<div className="modal-footer">{footer}</div>
</div>
);
}
// Usage
<Modal
header={<h2>Confirm</h2>}
footer={<button>OK</button>}
>
<p>Are you sure?</p>
</Modal>
React.Children Utilities
function List({ children }) {
// Count children
const count = React.Children.count(children);
// Map over children
const items = React.Children.map(children, (child, index) => {
return <li key={index}>{child}</li>;
});
// Check if children exist
if (!children) return null;
return (
<div>
<p>Items: {count}</p>
<ul>{items}</ul>
</div>
);
}
Cloning Children
function Parent({ children }) {
// Add props to all children
return React.Children.map(children, (child) =>
React.cloneElement(child, { className: "highlighted" })
);
}
// Usage
<Parent>
<p>First</p>
<p>Second</p>
</Parent>
Practice Problems
Create a reusable React component implementing Component Composition. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for Component Composition using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize Component Composition for performance. Consider memoization, code splitting, and bundle size.
Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysisQuiz
1. What is component composition?
2. What is a render prop?
3. What is the children prop?
4. What replaced render props?
Flashcards
Question
What is component composition?
Click to reveal answer
Answer
Building complex UIs by combining smaller components
Question
What is a render prop?
Click to reveal answer
Answer
A function prop that returns JSX
Question
What is the children prop?
Click to reveal answer
Answer
A special prop containing nested elements
Question
What are compound components?
Click to reveal answer
Answer
Components that work together using implicit state sharing
Question
What is Component Composition?
Click to reveal answer
Answer
Component Composition is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.Composition is preferred over inheritance
- 2.Render props share logic via function props
- 3.Children prop enables component nesting
- 4.Custom hooks replaced render props
- 5.Compound components share implicit state
Interview Tips
- •Explain composition vs inheritance
- •Show render prop pattern
- •Demonstrate children prop usage
Cheat Sheet
Cheat Sheet
Composition
function Layout({ children }) {
return <div>{children}</div>;
}
Render Props
<DataFetcher
render={({ data, loading }) => (
loading ? <Spinner /> : <List data={data} />
)}
/>
Children as Function
<Toggle>
{({ on, toggle }) => (
<button onClick={toggle}>{on ? "ON" : "OFF"}</button>
)}
</Toggle>
React.Children
React.Children.map(children, fn)
React.Children.count(children)
React.cloneElement(child, props)