Why apply Open/Closed principles in React component composition?
Tags: react, architecture, javascript, functional
Published: 2019-10-29 03:32:32

Have you ever looked at a messy piece of code and just wanted to burn it down? I know I have had :blush:. That’s why I started to learn software architecture. I started to think about working on a clean, scalable, reliable code base that makes development fun. After all, implementing new features should be exciting not stressful. 

In this article we are going to explore how we can take advantage of composition pattern and apply **Open/Close** principle (from **SOLID principles**) to design our applications so that they are easy to work with, expandable and enjoyable to code features.

> please note: this is the second part of my React design pattern series with SOLID principles. You can find the first part [here](https://dev.to/shadid12/can-you-apply-solid-principles-to-your-react-applications-46il)

#### **What is Open/Closed 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.

#### **How do we apply OCP in React?**

In **OOP** languages such as Java or Python this concept is applied through inheritance. This keeps the code **DRY** and reduces coupling. If you are familiar with Angular 2+ then you know that it is possible to do inheritance in Angular 2+. However, JavaScript is not really a pure Object Oriented language and it doesn’t support classical inheritance like OOP languages such as Java, python or C#. So whenever you are implementing an interface or extending a class in Angular 2+, the framework itself is doing some process in the background and giving you the illusion of writing **OOP** code. In React we don’t have that luxury. React team encourages functional composition over inheritance. **Higher Order Functions** are JavaScript's way of reusing code and keeping it DRY.

Let’s look at some code and see how we compose components and how we can follow open/closed principle to write clean, reliable code.
Below we have an `App` component that is rendering `OrderReport`. We are passing in a customer object as props.

```jsx
function App() {
  const customer = {
    name: 'Company A',
    address: '720 Kennedy Rd',
    total: 1000
  }
  return (
    <div className="App">
      <OrderReport customer={customer}/>
    </div>
  );
}
```
Now let’s take a look at our `OrderReport` Compoennet

```jsx
function OrderReport(props) {
  return (
    <div>
      <b>{props.customer.name}</b>
      <hr />
      <span>{props.customer.address}</span>
      <br />
      <span>Orders: {props.customer.total}</span>
      {props.children}
    </div>
  );
}
```
This component here has a little secrect ;). It doesn’t like changes. For instance let’s say we have a new customer object with couple more fields than the first one. We want to render additional information based on our new customer object that is passed as props. So let's take a look at the code below.

```jsx
const customerB = {
    name: "Company B",
    address: "410 Ramsy St",
    total: 1000,
    isEligible: true,
    isFastTracked: false
};
const customerC = {
    name: "Company C",
    address: "123 Abram Ave",
    total: 1010,
    specialDelivery: true
};
```
We added 2 new customer objects, they both have couple new extra keys. Let's say based on these keys we are required to render additional html elements in our components. So in our `App` component we are now returning something like this 

```jsx
return (
    <div className="App">
      <OrderReport customer={customer} />
      <OrderReport customer={customerB} />
      <OrderReport customer={customerC} />
    </div>
);
```
And we change our `OrderReport` component accordingly to render additional functionality based on passed props. So our component now looks something like this

```jsx
function OrderReport(props) {
  const [fastTracker, setFastTracker] = React.useState(props.isFastTracked);
  return (
    <div>
      <b>{props.customer.name}</b>
      <hr />
      <span>{props.customer.address}</span>
      <br />
      <span>Orders: {props.customer.total}</span>
      {props.customer.isEligible ? (
        <React.Fragment>
          <br />
          <button
            onClick={() => {
              setFastTracker(!fastTracker);
            }}
          />
        </React.Fragment>
      ) : null}
      {props.customer.specialDelivery ? (
        <div>Other Logic</div>
      ) : (
        <div>Some option for specialDelivery logic...</div>
      )}
      {props.children}
    </div>
  );
}
```
As you can see it already started to look very noisy. This is also violating the single responsibility principle. This component is responsible for doing too many tasks now. According to **open/closed** principle components should be open to extension but closed for modification, but here we are modifying too many logic at once. We are also introducing unwanted complexity in the code. To resolve this let’s create a higher order component to break up this logic.

```jsx
const withFastTrackedOrder = BaseUserComponent => props => {
  const [fastTracker, setFastTracker] = React.useState(props.isFastTracked);
  const baseElments = (
    <BaseUserComponent customer={props.customer}>
      <br />
      <button
        onClick={() => {
          setFastTracker(!fastTracker);
        }}
      >
        Toggle Tracking
      </button>
      {fastTracker ? (
        <div>Fast Tracked Enabled</div>
      ) : (
        <div>Not Fast Tracked</div>
      )}
    </BaseUserComponent>
  );
  return baseElments;
};
```
As you can see above that we created `withFastTrackedOrder` HOC that consumes an `OrderReport` component and adds in some extra logic and html.
 
Now all our fast tracked orders logic is encapsulated inside one `withFastTrackedOrder` component. Here `withFastTrackedOrder` adding additional functionality and extending our already written logic from `OrderReport`. Let's revert back our `OrderReport` to its minimal form like shown below.

```jsx
function OrderReport(props) {
  return (
    <div>
      <b>{props.customer.name}</b>
      <hr />
      <span>{props.customer.address}</span>
      <br />
      <span>Orders: {props.customer.total}</span>
      {props.children}
    </div>
  );
}
```

In our `App` we are doing rendering them like following now

```jsx
function App() {
  const FastOrder = withFastTrackedOrder(OrderReport);
  return (
    <div className="App">
      <OrderReport customer={customer} />
      <FastOrder customer={customerB} />
    </div>
  );
}
```

So there you have it. We have broken down the logic into two maintainable, clean components. `OrderReport` is now open for extensions but closed for modification.

Now let's assume our business rule requires us to render some extra html for customers with special orders. Can we extend our `OrderReport` again. absolutely we can. Let's create another HOC that will compose `OrderReport`.

```jsx
const withSpecialOrder = BaseUserComponent => props => {
  return (
      <BaseUserComponent customer={props.customer}>
        <div>I am very special</div>
        {props.children}
      </BaseUserComponent>
  );
};
```
`withSpecialOrder` component is consuming the OrderReport and adding the extra html in.
Now in our `App` we just do the following

```jsx
function App() {
  const FastOrder = withFastTrackedOrder(OrderReport);
  const SpecialOrder = withSpecialOrder(OrderReport);
  return (
    <div className="App">
      <OrderReport customer={customer} />
      <FastOrder customer={customerB} />
      <SpecialOrder customer={customerC} />
    </div>
  );
}
```
Beautiful, isn' it? we have composed our components in small chunks. We have kept them separated by logic and we are not rewriting same logic. All our components are open for extension. We are able to reuse the code and keep it **DRY**.
Let's take this idea a step further. Let's say now our business allows same day delivery service for some special Orders. We can write another higher order component to wrap our `SpecialOrderComponent` and add this additional logic in. Remember our components are always open for extension and closed for modification. So with the creation of a new HOC we are extending our existing component's functionality. Let's write this HOC.

```jsx
const withSameDayDeliver = SpecialOrderComponent => props => {
  return (
    <SpecialOrderComponent customer={props.customer}>
      <div>I am also same day delivery</div>
      {props.children}
    </SpecialOrderComponent>
  );
};
```

now apply this new HOC to our `App` like so

```jsx
function App() {
  const FastOrder = withFastTrackedOrder(OrderReport);
  const SpecialOrder = withSpecialOrder(OrderReport);
  const SameDayDelivery = withSameDayDeliver(withSpecialOrder(OrderReport));
  return (
    <div className="App">
      <OrderReport customer={customer} />
      <FastOrder customer={customerB} />
      <SpecialOrder customer={customerC} />
      <SameDayDelivery customer={customerC} />
    </div>
  );
}
```

Now as you can see we have created a pattern of using HOC in a way that they are always open to extension but close for complicated modification. We can add in as many HOC as possible and as our code grows in complexity we can even mix and match these HOCs. This keeps our code simple and enjoyable to work with. It keeps our logic encapsulated so changes dont affect the entire system. It also maintains code sanity in the long run. 

The contents of these articles are in progress and I am constantly updating them based on best practices in the industry and my personal experience. Your feedback is crucial, please leave a comment if you have something to say. Please follow me for new articles like this.

You can find the link of previous article of this series [here](https://dev.to/shadid12/can-you-apply-solid-principles-to-your-react-applications-46il). 
Please like this post if you enjoyed it, keeps me motivated :) 


Next we will discuss how **liskov's substitution** is applied in React component architecture. Stay tuned.