Component Composition in React
As discussed in Part 1 of the series on components and props, components are the building blocks in react application. The UI’s are represented in form of components that perform a definite task.
?
In this post, we will take a look at another important technique in react, known as component composition, the rationale behind composing components, implementation examples, the benefits, a brief overview of the advanced concepts of components composition, and finally conclude with key takeaways.
What is component composition?
The concept refers to the means of combining components (small or big) together to form a larger or complex user interface (UI). Specifically, it refers to the process by which UI’s are created by combining other UI(s) that are independent and reusable.
?
The rationale of composing components can be analogized to the approach of solving a problem based on the strategy of divide and conquer. This approach breaks a problem (in react context, breaks entire UI) into smaller sub-problems (in react context, as components), solving the sub-problems (in react, creating the component with logic and or props) and combining them (that’s, the created components) to get the desired solution (react application).
Consider a case where we will build an application dashboard with the header on the top, a sidebar on the left, a content area to the right of the sidebar, and the footer at the bottom of the page.? This task can be broken into having a header compared, a sidebar component, the main component, and a footer component. Later, all these components can be combine to produce the application dashboard as illustrated below.
// header.jsx
function Header() {
return (
<div>
<p>This is the header Component</p>
</div>
)
}
// sidebar.jsx
function SideBar() {
return (
<ul>
<li>
<a href=”#”>Dashboard</a>
</li>
<li>
<a href=”#”>Courses</a>
</li>
<li>
<a href=”#”>Payments</a>
</li>
</ul>
)
}
// footer.jsx
function Footer(){
return (
<div>
<p>This is the footer section</p>
</div>
)
}
With the various components created as shown above, we can create the application layout component that combines all the previous components as shown below.
// AppLayout.jsx
function AppLayout(){
return (
<div>
<Header />
<SideBar />
<Footer />
</div>
)
}
The AppLayout component composes these components to form a new component, which can be use to embed the entire App component.
In Part 1 on components and props, we discussed that to pass or share data/events across components, we use props to achieve that. In composing components, components too can be shared as props to other components using the special children prop, which acts as a placeholder for placing other components, as shown below.
// main.jsx
function Main({children}){
return (
<div>
{children}
</div>
)
}
The main component takes the children prop wrapped in curly braces, which is then placed in the return statement in between the div element. This serves as a placeholder for other components. With this implementation, we can modify the AppLayout component to include the main component, which will serve as the content area, as shown below:
领英推荐
// AppLayout.jsx
function AppLayout(){
return (
<div>
<Header />
<SideBar />
<Main>
<Outlet />
</Main>
<Footer />
</div>
)
}
Notice the inclusion of the main component in the AppLayout component. It was included the normal way HTML elements are included with opening and closing tags. In between, we included another component called Outlet which comes from react-router library (to be discussed later).
Avoiding Props Drilling with Component Composition
Additionally, composing components can also solve the problem of props drilling. Props drilling refers to the process where a prop is pass through several components until it gets to the right or target component, which in turn affects performance of a react application, thus causing unnecessary re-renders.
To demonstrate the issue of prop drilling, let’s assume we have a component called Profile that accepts the username of the currently logged-in user. However, the username leaves in the dashboard component. Therefore, to use the username we pass the username as props to all the intermediate components, so that we can access it in the Profile component as shown below.
// Dashboard.jsx
function Dashboard(){
const { username } = useAuth();
return (
<div>
<Header username={username} />
<Main>
<Outlet />
</Main>
</div>
)
}
// Header.jsx
function Header({username}){
return (
<div>
<Logo />
<SearchBar />
<Navigation username={username} />
</div>
)
}
// Navigation.jsx
function Navigation({username}){
return (
<div>
…
<Profile username={username} />
</div>
)
}
// Profile.jsx
function Profile({username}){
return (
<div>
<Avatar />
<p>{username}</p>
</div>
)
}
Notice how the username prop is passed from the Dashboard component down to the Profile component which will cause performance issues as mentioned earlier. This problem can be solved in a variety of ways in react, such as using component composition, using the useContext hook, etc.
Let’s see how this can be solve with component composition by minimizing the number of intermediaries components, thus reducing the number of re-renders as shown below.
Now, instead of passing the username props from a dashboard down to a profile component, we only pass it from the dashboard to a header component. The header component which contained the navigation component has now taken another prop called profile where its value is the profile component, using the username prop obtained from the dashboard component.
// Header.jsx
function Header({username}){
return (
<div>
<Logo />
<SearchBar />
<Navigation profile={<Profile username={username} /> } />
</div>
)
}
This has the benefit of improving the performance of the application as a whole. Therefore, composing components can be used in different scenarios apart from what we have seen above. Another common approach is to create a component, say container, that wraps other application UI’s. This approach gives us a number of benefits, such as:
However, this is the most basic concept of component composing. Other advanced concepts relating to this concept such; Higher order component (HOC), render props, compound component, etc. will be discuss on the section of react patterns.
Conclusion
Component composition is a means of breaking complex UI into smaller and reusable components that can be later combine to form large or complex UI. Thiis gives us the benefits of re-usability, separation of concern, optimizing performance and flexibility.