DEV Community

Martin Saposnic
Martin Saposnic

Posted on

Open/Closed principle on React.js

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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>
    )
}
Enter fullscreen mode Exit fullscreen mode
function Bycicle() {
    return (
        <div className='vehicle'>
            <h1>2 wheels</h1>
            <h1>powered by humans!</h1>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode
function Plane() {
    return (
        <div className='vehicle' >
            <h1>3 wheels</h1>
            <h1>A lot of doors</h1>
            <h1>Gas</h1>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

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> },
}
Enter fullscreen mode Exit fullscreen mode

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.

  1. They all share the same class
  2. 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>
    )
}
Enter fullscreen mode Exit fullscreen mode

Now we have our generic component. Let's use it!

function Car() {
    return (
        <Vehicle 
           wheels='4 wheels' 
           doors='5 doors' 
           fuel='electric'
        >
    )
}
Enter fullscreen mode Exit fullscreen mode
function Bycicle() {
    return (
        <Vehicle 
           wheels='2 wheels'
           fuel='human'
        >
    )
}
Enter fullscreen mode Exit fullscreen mode
function Plane() {
    return (
        <Vehicle 
           wheels='3 wheels'
           doors='a lot'
           fuel='gas'
        >
    )
}
Enter fullscreen mode Exit fullscreen mode

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> },
}
Enter fullscreen mode Exit fullscreen mode

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

Real devs don't use if

Top comments (0)