Use the Open/Close Principle to Write Clean, Modular React Code
By: Shadid Haque
Tags: JavaScript, Software Architecture, Web Development, React, Programming

Photo by Pankaj Patel on Unsplash

In this article I will show you how to apply Open/Close principles (a very well know concept in software architecture) into your React components and write clean, reusable and modular code with ease.

What is the Open/Close Principle?

In object-oriented programming, the open/closed principle states “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”; that is, such an entity can allow its behaviour to be extended without modifying its source code.

But how is this useful in a React app?

To understand how this could be useful in a React app, let’s start with a practical problem. Let’s say we are building a warehouse application. In this application we have a component called OrderReport which is responsible for handling user orders.

Here’s how our code would look for this sample component

Now let’s say our business rules have changed. Management has decided that now our warehouse company will deal with three types of orders. Some customers can choose to have a special delivery option or a fast delivery option.

Here’s how the newer customers data would look like.

const customerB = {

name: "Company B",

address: "410 Ramsy St",

total: 1000,

isFastTracked: true

};

const customerC = {

name: "Company C",

address: "123 Abram Ave",

total: 1010,

specialDelivery: true

};

Management gave us a requirement that we still need to support all the old customer objects. However, if a customer object has a isFastTracked flag or a specialDelivery flag then we would need to perform some additional logic, for instance we need to make an additional api call and render additional information.

From a developer’s perspective this seems like an easy implementation. We can implement these additional logic to our existing OrderReport component like below.

As you can see the component started to get noisy here. We are assigning too many responsibilities to this single component. Moreover, this component is hard to test and not very reusable. Now what if after a week or so our business needs to add five new type of delivery options with slightly different business logic. Now we have a problem, updating this component will be a tedious and an error prone task. Software is easier to maintain when we design individual software components/modules open for extension but closed for modification. That’s what the Open/Closed principle is. We want to avoid major changes in our core OrderReport while making it extensible so that its functionality can be used in other components.

In classical object oriented programming this is done through inheritance. In languages like Java, C# and Python we can extend an existing class to add additional functionality to code. However, in JavaScript we can do this simply with a function composition. Let’s take a look at how we can apply this. In the example below I created a new higher order component that is only responsible for handling fast-tracked orders

As you can see it receives a base component as an argument and adds additional functionality to it. In this scenario our base component is the old OrderReport component. We can revert back to the simplest form of OrderReport component like shown below.

And in our App component every-time we identify a fast tracked order we can compose it with withFastTrackedOrder higher order component.

Similarly we can create a higher order component for the special order.

And change our app component to reflect this changes like below

Now this is much cleaner than the first solution we came up with. Now all our component are open to extension. We can easily create more HOC (Higher Order Components) to extend the functionality of each of these components and we don’t have to modify the core functionality. This way our application is more agile to future changes.

Moreover, these smaller components are responsible for one specific task which makes them easier to maintain, reuse in different parts of the app if we want to and easier to write unit tests.

That is all for today 😀, hope you enjoyed this article.