One of the great things about react is the ability to create as many components as we want. Breaking down codes into components allows for maintainability and flexibility. I have always tried, to the best of my knowledge to break down every small UI unit into its self-contained components. This allows to abstract the inner functionality and presents a well-composed component to utilize on every page where it's needed.
Many of my fellows react developers try to create atomic components for flexibility but I feel, most of them are breaking every component just because they've been taught to do so since they started learning react . Every react instructor, developer and even the very core of the react principles encourage the component-based approach. However, developers are often misguided and when trying to break components they create convoluted structures where states are drilled down or distributed to multiple co-dependent components.
These messed-up architecture are often the result of not knowing which components should be holding which states. As a react developer, one should always be vigilant on where states should be declared and when those need to be updated. I have always felt one of the most difficult, yet simple, concepts of react is to know the ownership of states. That's often because there's no such hard and fast rule and usually boils down to how the developer chooses/knows to organize states.
To explain, where component compositions go wrong, let's look at the example below. The example was in fact what I encountered on one of my colleagues' codes and that is what triggered me to write a blog on the topic.
So, the requirement was to create accordions which are draggable and contains within itself components which are also draggable and editable. Firstly we created an accordion component using context api using concepts of compound components. The implementation of the accordion looked something like this:
<Accordion>
<AccordionSummary>
..... Display when accordion is collapsed
</AccordionSummary>
<AccordionDetails>
..... Display when accordion is expanded
</AccordionDetails>
</Accordion>
Simply stating, AccordionSummary wrapped the elements which are always visible whereas the AccordionDetails are hidden or viewed by collapsing or expanding the accordion.
Now for the sake of breaking down components and making it more manageable, a new component was created and the data needed to render the draggable accordion was passed down as props. No harm done.
<AccordionContainer items={...itemsData}/>
The AccordionContainer was further broken down and DraggableAccordion component was created and each data item to be rendered as accordion was passed down as a prop.
// AccordionContainer Component
<DraggableWrapper>
{items.map(item=><DraggableAccordion item={item}/>)}
</DraggableWrapper>
Then the DraggableAccordion contained the actual accordion to be rendered.
// DraggableAccordion Component
<Accordion>
<AccordionSummary>
.. item Summary
</AccordionSummary>
<AccordionDetails>
... item Details
</AccordionDetails>
</Accordion>
When I looked at the code and asked what his intention was while creating such nested components, his answers were pretty clear, "Components are what makes UI units more composable and flexible". But he didn't foresee what havoc such components wreak when state manipulations are involved. Now see, the top component which was responsible for rendering the data also needed to keep track of editable stated within each accordion. While, it was fairly easy to break down to components which bore single responsibility , it started getting difficult to keep track of state changes throughout the nested blocks.
Later on, after realizing that state needed to be updated on the parent component, the whole architecture of draggable accordion was recomposed to a single component and keeping track of state changes was so much easier.
<DraggableWrapper>
{
items.map(item=>(
<DraggableItem>
<Accordion>
<AccordionSummary>
..... Display items summary when accordion is collapsed
</AccordionSummary>
<AccordionDetails>
.......Display items details when accordion is expanded
</AccordionDetails>
</Accordion>
</DraggableItem>
)
}
</DraggableWrapper>
I understand that recomposing all those components back to it's parent component created a bulky structure. But in doing so, we managed to stop the unnecessary flows of states. And regarding the bulk, we rather broke down other elements which didn't require any deep prop drilling.
So before breaking down components I always recommend foreseeing which component should own the states.