Master modern component structure, styles, and reusable UI systems.
Welcome to Component Design Patterns! In this chapter, we'll explore various approaches to building maintainable and reusable React components. Understanding these patterns will help you create more organized, scalable, and efficient UIs.
React applications often use two types of components: presentational and container. Understanding the difference between them is crucial for creating clean, maintainable code.
// Presentational Component
function UserCard({ user }) {
return (
<div className='user-card'>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
// Container Component
function UserList() {
const [users, setUser] = useState([]);
useEffect(() => {
fetchUsers().then(data => setUser(data));
}, []);
return (
<div className='user-list'>
{users.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}
A compound component is a set of related, reusable components that work together to fulfill a specific purpose. This pattern promotes code reuse and modularity.
// Compound Component: Pagination
function Pagination({ currentPage, totalPages }) {
return (
<div className='pagination'>
{renderPrevious(currentPage)}
{renderPageNumbers(currentPage, totalPages)}
{renderNext(currentPage, totalPages)}
</div>
);
}
function renderPrevious(currentPage) {
if (currentPage === 1) return null;
return <button onClick={() => setCurrentPage(currentPage - 1)}>โ Previous</button>;
}
// And other helper components...
A higher-order component is a function that takes a component and returns a new component with enhanced functionality. HOCs are powerful for reusing logic across multiple components.
// HOC: withLoadingState
function withLoadingState(Component) {
return function WrappedComponent({ fetchFn, ...props }) {
const [loading, setLoading] = useState(true);
useEffect(() => {
async function loadData() {
await fetchFn();
setLoading(false);
}
loadData();
}, []);
return (
<div className='loading-container'>
{loading ? (
<div className='loader'>Loading...</div>
) : (
<Component {...props} />
)}
</div>
);
};
}
The render props pattern is a way to reuse component logic without changing the component's render output. It provides flexibility by allowing components to customize how they display data.
// Render Props Example
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => setData(data));
}, [url]);
return (
<div className='data-container'>
{render({ data })}
</div>
);
}
// Usage in another component
<DataFetcher url='/api/users'
render={({ data }) => (
<div className='user-list'>
{data.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
)} />
When it comes to styling React applications, there are multiple approaches you can take. Each has its own pros and cons, and choosing the right one depends on your project's needs.
// Example of CSS Module
.example {
padding: 20px;
}
// Styled Component Example
const Button = styled.button`
background-color: ${props => props.primary ? '#4CAF50' : '#f1f1f1'};
`
Question 1 of 11