Skip to content
Roberto's Corner

Open-closed SOLID principle in React

react, open-closed principle, composition, inheritance3 min read

Recently I read a very good article¹ published by a co-worker which demonstrate how to apply SOLID principles on react applications. In the article, the Open-closed principle is demonstrated using composition which I believe to be the de facto, and the best method to extend React components, although the principle usually refers to classical inheritance².

So I decided to write this article to exemplify how to apply the Open-closed principle using both inheritance and composition.

As a naive example let's create a simple Button component that gets a single prop name. A new component, SmileyButton should extend the previous and get a new prop smiley. This prop will be concatenated to the button's name.

If to be compliant with the Open-closed principle, it required to use class-based inheritance, this is how we could design our example:

class Button extends React.Component {
getButtonText() {
return this.props.name;
}
render() {
return <button>{this.getButtonText()}</button>;
}
}
// Note that SmileyButton extends Button instead of React.Component
class SmileyButton extends Button {
getButtonText() {
const text = super.getButtonText();
const smiley = this.props.smiley || "";
return `${text} ${smiley}`;
}
}
// Client code
const HappyButton = <SmileyButton name="Happy Button" smiley=":D" />

The principle is properly applied as we didn't modify the original Button component and still added a custom behaviour!

What about using composition? Composition is about what it does over what it is as in inheritance. Saying that, let's create functional components and compose them to achieve the same result as before:

const Button = ({ name }) => <button>{name}</button>
const SmileyButton = ({ name, smiley = "" }) => {
const happyName = `${name} ${smiley}`;
return <Button name={happyName} />;
};
// Client code
const HappyButton = <SmileyButton name="Happy Button" smiley=":D" />

We achieved the same result as the previous example! We just composed the Button component with the custom behaviour that we need! In my opinion, composition is more flexible, clean and future proof than inheritance, and joining it with react functional components... Damn, it's beautiful!

Note composition is not exclusive of Functional Programing paradigm, it's often recommended using it over inheritance in Object-Oriented Programming.

We aren't finished yet! There is another way of extending a component behaviour by taking a functional approach again. This time original Button will provide an API that lets us change its own behaviour:

const Button = ({ getButtonText = () => "" }) =>
<button>{getButtonText()}</button>
// Client code
const HappyButton = <Button getButtonText={() => "Happy Button :D"} />

In this last example, we don't need to compose the button component as the client will provide the behaviour right away.

Here is a sandbox with working samples for all examples provided above:

Wrapping up, the composition is the way to go when extending functionality of React components, and guess what, High Order Components (HOCs) are a perfect example of composition. Other than that, remember that React class components are deprecated and will fade away soon! Leave a comment bellow and share your feedback!

References:
  1. Applying SOLID To React
  2. Open-closed principle
author @RobertoRJ

Thanks for reading my article. I am Roberto and I'm based in Madeira Island, Portugal. Get in touch via @RobertoRJ or email me at em.susejotrebor@tniopyrtne

© 2024 by Roberto's Corner - Blog posts about Web and Software Engineering
Built with Gatsby - Theme by LekoArts