Imagine that you have the following requirement:
Create a Component in React.JS that displays properties from a Car, e.g. number of wheels, number of doors, if it's electric or has gas, etc.
So you start creating your component like this:
function Car() {
return (
<div>
<h1>4 wheels</h1>
<h1>5 doors</h1>
<h1>electric</h1>
</div>
)
}
Simple right? But wait! There is a new requirement. We need to show the same properties for another vehicle -> a bicycle!
So now... what do we do? We can add a bunch of ifs!
function Vehicle(props) {
return (
<div>
{
props.type === 'car' ?
<h1>4 wheels</h1> :
<h1>2 wheels</h1>
}
{
props.type === 'car' &&
<h1>5 doors</h1>
}
{
props.type === 'car' ?
<h1>electric</h1> :
<h1>powered by humans!</h1>
}
</div>
)
}
I'm sure you already guessed that this can become a mess if we add a new type of vehicle.
This happens because this component is not following the Open/Closed principle. Why?
Open/Closed principle
The traditional definition is "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".
And this exact definition can be applied to Components!
So here comes a new iteration (not the last one):
What if we create a component for every type of vehicle?
Let's see:
function Car() {
return (
<div className='vehicle'>
<h1>4 wheels</h1>
<h1>5 doors</h1>
<h1>electric</h1>
</div>
)
}
function Bycicle() {
return (
<div className='vehicle'>
<h1>2 wheels</h1>
<h1>powered by humans!</h1>
</div>
)
}
function Plane() {
return (
<div className='vehicle' >
<h1>3 wheels</h1>
<h1>A lot of doors</h1>
<h1>Gas</h1>
</div>
)
}
And then the only thing we need to do is map every component to its type:
const vehicleComponents = {
'car': () => { return <Car> },
'bicycle': () => { return <Bicycle> },
'plane': () => { return <Plane> },
}
But wait!
There is a possibility that someone is saying:
Those components are looking disturbingly the same
You are right! That can be generalized!
Basically we have to identify what's similar on the three components.
- They all share the same class
- They all display properties in H1s
So we create a new component that can receive this properties as props!
interface VehicleProps {
wheels: string
doors: string | undefined // bicycle does not have doors!
fuel: string
}
function Vehicle(props: VehicleProps) {
return (
<div className='vehicle' >
<h1>props.wheels</h1>
{props.doors && <h1>props.doors</h1>}
<h1>props.fuel</h1>
</div>
)
}
Now we have our generic component. Let's use it!
function Car() {
return (
<Vehicle
wheels='4 wheels'
doors='5 doors'
fuel='electric'
>
)
}
function Bycicle() {
return (
<Vehicle
wheels='2 wheels'
fuel='human'
>
)
}
function Plane() {
return (
<Vehicle
wheels='3 wheels'
doors='a lot'
fuel='gas'
>
)
}
So now we have our refactored and generic components ready to be used and maintained!
So let's say now we have a new type of vehicle. The only thing we have to do is create the component that reuses the Vehicle component, add the new component to the vehicleComponents and you are done!
const vehicleComponents = {
'car': () => { return <Car> },
'bicycle': () => { return <Bicycle> },
'plane': () => { return <Plane> },
'new-vehicle': () => { return <NewVehicle> },
}
Now let's say you want to change the styles of how the vehicles display. You only have to modify the styles in one place -> the Vehicle component.
And that is the magic and beauty of the Open/Closed principle :)
Less IFs, more smiles
Top comments (0)