Component structures are very unopinionated in terms of how they receive data - is it via props, or do they handle their own data/business logic? How your components are designed largely decide the fate of our application, onto how people are going to reuse the components, are they actually reusable or not, how much of a refactor does a component require before plugging in, how easier is it to plug the component and just use it. This blog solely focus on Data Driven Components - components which depend upon data from APIs and are not just UI components like buttons or list. I personally prefer to call these components widgets - (by definition - an application, or a component of an interface, that enables a user to perform a function or access a service.)
For this blog, let’s take an example for Product Cards from an e-commerce website that just simply shows the price, an image and the name of the product. Something similar to this 👇🏻
Now let’s discuss about the various ways I can write this component.
Component Handling the API Calls
This is a very naive way of creating any data driven component - let the component handle the data calls. So for our example, the component will accept a prop for the product id and then makes an API call to pull the details.
Pretty slick right? No, actually not ☹️, this type of a component may work fine, where there is only 1 product has to be shown on the page, but assume there is a list of products you wanna show? Something like this 👇🏻
In this case, you won’t like to make multiple API calls for all of the products, but rather you would write your components something like ⏭️
Data through Props
The usual way, how most of us have been writing components in React or with any UI Framework - by passing down the data required for UI rendering via props.
Perfect 😃, the good part with this is that at least the ProductCard
component is reusable 🎉 but hold on, now you see the problem, this so called widget - ProductList
is kind of coupled with the /all-products
API, what if I wanted to use this list component for recommended products as well or maybe on the search page as well?
So to fix this issue, we can also pass the API endpoint via a prop and handle the returned data, like this →
Which is good, but this is only possible when we are assuming that all of these APIs are gonna return the data is same shape.
And also does this way of writing ProductList
component makes it harder to test - no matter integration test or e2e test, you would have to pass a mock service API to the prop and let it make fetch calls in test environments, which I believe is not required for testing React UI Components.
Data Agnostic Components
I believe a much better solution to the above problem with a very standardised format of writing your React code is to have all components data agnostic - not worrying about how data is fetched, where is it coming from. Any component should have one and only one job - render the UI based on whatever props are there. So now our whole page will look like this -
The best part about this way of writing your React components is that they are Pure functions - will always render the same UI given the same props, they are easier to test, and most of all they are reusable.
The only problem I encounter while writing react components this way is that having the data layer at pages
level components brings a cumbersome job of maintaining the loading
and error
states for these APIs. So the solution to that is as usual passing the loading and error states as props to the component, or we can also check the actual data props itself - if it is undefined
→ we can assume it is loading, if it is of type error
then definitely an error or else the usual UI. Something like
Conclusion
Let me conclude this whole confusion by stating my opinion - the way I write my application code:
pages/Home.jsx
→ Should contain the basic layout of the page, as well as the data fetching part (no matter if you use some state management for that) - so basically the orchestration of different widgets 🎼, from our example this module is just thePage
component.widgets/ProductList.jsx
→ A compound structure of your basic UI components - like buttons, images, etc., from our example theProductList
component.components/Product.jsx
→ The most atomic level of our UI element, which you can plug into forming a complex widget.
In layman chemistry 🧪 terms, components
can be referred to as atoms
⚛️, widgets
can be termed as molecules
while your pages
can be as your tissues/organs
🫁