Modularising React apps
Introduction:
React being a library doesn't provide a concrete structure to handle and modularise your code as done by other frameworks. Even though this gives flexibility to the developers to handle and modularise their code as per requirement, this becomes a nightmare especially when:
The application grows in size
Many teams working on different parts of UI and following different structure
Codebase increases
Overlap between different team's deliveries
Hence maintaining a structure and modularising your code becomes very important
The Problem:
export const EKart = () => {
const [items, setItems] = useState<Items>([]);
useEffect(() => {
const fetchItems= async () => {
const url = "https://kart.com/api/items";
const response = await fetch(url);
const items: Items[] = await response.json();
setItems(items);
};
fetchItems();
}, []);
return (
<div>
<h3>Shop Items</h3>
<div>
{ items.map((item) => {
{item.count === 0 ? null :
<div key={item.id}>
<h3>{item.name}</h3>
<div> {item.count} </div>
<div> {item.currency === "dollar" ? "$" : "Rs" } </div>
</div>
}
}
</div>
</div>
);
};
The above code even though is perfectly valid in react has some issues . It's not modularised with no separation of concern
This single component handles:
Plain view
Business logic
API and response handling
State handling
Conditional rendering
It's hard to read and understand the above code. Testing becomes extremely difficult as the business logic is coupled within the view. When such code grow in size throughout the codebase, maintenance becomes a nightmare.
What if I say that the modularisation can do this magic to the above code 😯
Let's modularise:
1) APIs:
Extract out your APIs to a separate file and return a promise resolved data.
const getItems = async () => {
const url = "https://kart.com/api/items";
const response = await fetch(url);
const items: Items[] = await response.json();
return items;
};
2) Custom hooks:
You can design a custom hook to handle and call your API based on certain conditions. The retrieved result can be set, handled and managed using states.
const useItems = () => {
const [items, setItems] = useState<Items>([]);
useEffect(() => {
const fetchItems= async () => {
const itemsResp = await getItems()
setItems(itemsResp);
};
fetchItems();
}, []);
return { items };
}
3) Presenters:
Presenters would contain all the pure business logic in javascript/typescript.
const isCountZero= (item: Item) => {
return item.count === 0;
}
const getCurrency = (item: Item) => {
return item.currency === "dollar" ? "$" : "Rs";
}
4) Types:
This file would include all the typescript types used in your code flow.
type Item = {
id: number;
name: string;
count: integer;
currency: string;
};
type Items = Item[];
5) Presentation:
The presentation would contain all the pure view related logic that can be rendered in the screen at the end. This would basically have your react component (.jsx, .tsx) files.
export const DisplayItems = (items) => {
<div>
{ items.map((item) => (
<DisplayItem
name = {item.name}
count = {item.count}
currency = {getCurrency(item)}
key={item.id}
/>
}
</div>
}
export const DisplayItem = ({ name, count, currency, key }) => {
if(isCountZero(count)) return null;
return (
<div key={key}>
<h3>{name}</h3>
<div> {count} </div>
<div> {currency} </div>
</div>
)
}
Finally you can import the hooks and components in the base component to render the details in the view
export const EKart = () => {
const { items} = useItems();
return (
<div>
<DisplayItems items = {items} />
</div>
);
};
Now as you can see we have boiled down our complex single component into a leaner simple component with modularisation. Moreover, each and every extracted item is unit testable and following separation of concern.
Benefits of Modularisation:
Enhanced Maintainability
Code Reusability
Readability
Improved scalability
Ease in tech stack migration
Testability
Loose coupling of business logic and views
Do follow me on LinkedIn for more such blogs and technical contents. Thank you ☺️
References:
https://martinfowler.com/articles/modularizing-react-apps.html#IntroductionOfThePaymentFeature
https://refactoring.guru/design-patterns/typescript
https://www.patterns.dev/react/