Part1 (Layout Components)
A while ago, I have posted on Linkedin about Design patterns and what they are. Today, I want to talk about react design patterns and specifically one design pattern in React called Layout Components.
✨ Design Pattern: Simply I can say it is the best solution to an existing problem; so, in another word in react I can say it the best practice to handle a particular situation in react.
As we all know that react is an unopinionated Library for creating amazing UIs, so there is not just one good way to do things.
But there are some certain best practices which following them will help you to write clean, best performant and structured code. now let’s talk about our first react design pattern.
✨Layout Components: Layout components are primarily used for rendering & arranging other react components on a page, and it specifies where its child components should locate and how. They are one of the most common components which we deal as a react developer in daily basis. Some examples of layout components are split-screens, Lists, Modals and etc.
Split-screens
They are components that split the screen (page or a spot) into smaller pieces and place other desired components into those specified areas. Take a look at an example of a split-screen component which divides the page into two sections(Left & Right) and its efficient way of doing it:
import { Children, ReactNode } from "react";
export const SplitScreen = ({
leftWidth,
rightWidth,
className,
children,
}: {
className?: string;
leftWidth?: number;
rightWidth?: number;
children: ReactNode;
}) => {
const [left, right] = Children.toArray(children);
return (
<div className={`flex w-full ${className}`}>
<div style={{ flex: `${leftWidth}` }}>{left}</div>
<div style={{ flex: `${rightWidth}` }}>{right}</div>
</div>
);
};
SplitScreen.defaultProps = {
className: "",
leftWidth: 6,
rightWidth: 6,
};
and here is how we can use it in our app.tsx file.
import { SplitScreen } from "./LayoutComponents/SplitScreen";
const LeftScreen = () => (
<div className="bg-blue-700 text-white h-full">Left Screen</div>
);
const RightScreen = () => (
<div className="bg-orange-500 text-white h-full">Right Screen</div>
);
export default function App() {
return (
<div className="w-full h-full">
<SplitScreen className="min-h-[600px]" leftWidth={8} rightWidth={4}>
<LeftScreen />
<RightScreen />
</SplitScreen>
</div>
);
}
if you take a look at app.tsx file, you will see that we have some props that make our split-screen components fully reusable in any way and with any children.
now let’s look at the above code result in our browser:
Lists:
lists are also one of the most common scenario we face a react developer in our daily basis. Lists came under the layout components because it arranges the list items in a page. so, doing it the right and efficient way to be performant and fully reusable is what we take a look at now:
import { FC } from "react";
interface ListRendererPropsType {
items: unknown[];
resourceName: "product" | "person";
ItemComponent: any;
}
export const ListRenderer: FC<ListRendererPropsType> = ({
items,
resourceName,
ItemComponent,
}) => {
return (
<ul>
{items.map((item, i) => (
<ItemComponent key={i} {...{ [resourceName]: item }} />
))}
</ul>
);
};
The above component is a reusable List renderer component which renders any kind of list item inside itself and for this demonstration I used it just with products and people, you can add any other use cases you want.
Here bellow is how we use it
import { ListRenderer } from "./LayoutComponents/ListRenderer";
type PersonItemPropsType = {
person: {
fullName: string;
age: number;
occupation: string;
};
};
type ProductItemPropsType = {
product: {
name: string;
price: number;
quantity: number;
};
};
const ProductItem = ({ product }: ProductItemPropsType) => (
<li className="w-full p-5 bg-orange-200 my-2">
<h1>{product.name}</h1>
<div>Price: ${product?.price}</div>
<div> Quantity: {product?.quantity}</div>
</li>
);
const PersonItem = ({ person }: PersonItemPropsType) => (
<li className="w-full p-5 bg-blue-300 my-2">
<h1>{person.fullName}</h1>
<div>age: {person?.age}</div>
<div> occupation: {person?.occupation}</div>
</li>
);
const people = [
{
fullName: "John Doe",
age: 35,
occupation: "AI Engineer",
},
{
fullName: "Baqir Nekfar",
age: 23,
occupation: "Frontend Engineer",
},
{
fullName: "Sara Smith",
age: 27,
occupation: "UI/UX designer",
},
];
const products = [
{
name: "Dinning Table",
price: 100,
quantity: 10,
},
{
name: "Programming Chair",
price: 200,
quantity: 20,
},
{
name: "Sofa",
price: 1000,
quantity: 100,
},
];
export default function App() {
return (
<div className="w-full h-full p-10">
<h1>List of people:</h1>
<ListRenderer
ItemComponent={PersonItem}
resourceName="person"
items={people}
/>
<h1>List of products:</h1>
<ListRenderer
ItemComponent={ProductItem}
resourceName="product"
items={products}
/>
</div>
);
}
Note: I have put all the information like the list of people, products and also each person Item component and product item component in one page for simplicity, but in real scenario the data would come from an external API with the custom hook that I will talk about it in my later articles and the components will be put outside in their own files.
Now lets look at the result of it:
Modals
another common type of Layout components are Modals. Modals wrap all its contents and shows them in another layer upper than the screen. To create a reusable & efficient Modal component there is no need to use third party packages. Its easy as bellow:
import { FC, ReactNode } from "react";
interface ModalPropsType {
children: ReactNode;
visible: boolean;
requestToClose: () => void;
}
export const Modal: FC<ModalPropsType> = ({
children,
visible,
requestToClose,
}) => {
if (!visible) return null;
return (
<div
className="fixed inset-0 bg-black/70 flex justify-center items-center"
onClick={requestToClose}
>
<div
className="relative min-w-[900px] p-5 min-h-[400px] bg-white"
onClick={(e) => e.stopPropagation()}
>
<button onClick={requestToClose} className="text-red-500">Hide Modal</button>
{children}
</div>
</div>
);
};
And here bellow is how I used it with the people list rendered inside the modal:
import { useState } from "react";
import { ListRenderer } from "./LayoutComponents/ListRenderer";
import { Modal } from "./LayoutComponents/Modal";
type PersonItemPropsType = {
person: {
fullName: string;
age: number;
occupation: string;
};
};
const PersonItem = ({ person }: PersonItemPropsType) => (
<li className="w-full p-5 bg-blue-300 my-2">
<h1>{person.fullName}</h1>
<div>age: {person?.age}</div>
<div> occupation: {person?.occupation}</div>
</li>
);
const people = [
{
fullName: "John Doe",
age: 35,
occupation: "AI Engineer",
},
{
fullName: "Baqir Nekfar",
age: 23,
occupation: "Frontend Engineer",
},
{
fullName: "Sara Smith",
age: 27,
occupation: "UI/UX designer",
},
];
export default function App() {
const [modalVisible, setModalVisible] = useState<boolean>(false);
return (
<div className="w-full h-full p-10">
<h1>List of people shown in a Modal:</h1>
<button
className="text-orange-500 font-bold"
onClick={() => setModalVisible(true)}
>
See List
</button>
<Modal
visible={modalVisible}
requestToClose={() => setModalVisible(false)}
>
<ListRenderer
ItemComponent={PersonItem}
resourceName="person"
items={people}
/>
</Modal>
</div>
);
}
Last but not least, lets take a look at the result of it:
Alright, In this article we learnt about the first react design pattern called Layout components. We talked and gave some examples of react layout components like spit-screens, lists & modals and their properly usage.
🙏 Thanks for reading! If you liked it, stay tunned with JS Dojo news letter on Linkedin, give a clapp in medium and share it with in your own community in daily.dev.
Also, If you want to be updated with the tools, insights and my everyday learning join my daily.dev squad: JS Dojo!