# Building SOLID React Apps

## Introduction

SOLID is a set of object-oriented design principles. These principles establish practices for developing classes in object-oriented programming.

> The SOLID principles aims to serve one purpose which is to create reusable, maintainable, flexible and easy-to-understand code in order to facilitate collaborative software development.

Despite being created for object-oriented programming, the principles of SOLID can be applied in React applications, although they may manifest slightly differently due to the nature of React being a library for building user interfaces rather than a strict object-oriented programming paradigm. In this article, you would see how each SOLID principle can be applied in the context of React.

### S - Single Responsibility Principle (SRP):

Components should have a single responsibility. Separate concerns such as rendering, state management, and data fetching into different components or hooks to keep each component focused and maintainable.

For example, a Product Card component on an eCommerce website responsible for rendering product information (name, image, description) should only handle rendering the product and not perform unrelated tasks such as fetching data or component actions (e.g. on button click). Container components which sit above presentational components like ProductCard should handle data fetching, application logic, and pass down the data as props to presentational components.

From the image below, [the code at the top](https://gist.github.com/maryojo/0c8f146a4ac28de768161a6c0906a453), violates SRP because it has two concerns in the component code:

* Displaying product details
    
* Handling cart interactions (adding the product and managing errors).
    

The [corrected code](https://gist.github.com/maryojo/7740f3dbf1fae3371fd77fa38312aeff) shows separated functionalities:

* The ProductCard now renders only product information (presentational information).
    
* The parent/container component handles interactions and other actions related to the children components.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1716017914167/5b7b3735-37ac-4ce0-9546-fe110db5f9c2.png align="center")

### O - Open/Closed Principle (OCP):

You should be able to extend the behavior of a component without needing to directly modify its source code. Use higher-order components (HOCs), composition or props to customize and extend the behavior of components rather than modifying their internals directly. This is closely related to the first principle.

Still using the `ProductCard` component, the first part of the image shows the component which violates the OCP principle, this makes it hard to reuse the `ProductCard` across the application because it now has a local `handleAddCart` function which cannot be customized.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1716017003485/27030696-8df1-43a2-a369-c5255037b319.png align="center")

### L - Liskov Substitution Principle (LSP):

This principle emphasizes that objects (components in this case) of a superclass should be replaceable with objects of its subclasses without causing unexpected behavior. Create reusable components with well-defined props that can be easily substituted for one another in different parts of the application.

An example of this is a basic `Button` component that renders a button with a label and onClick handler. You then create a derived component `PrimaryButton` that inherits from `Button` and adds styles for a primary style button.

The first image which violates LSP, shows how the `PrimaryButton` directly modifies the props passed to the Button component - adding a style attribute. If another component expects a plain `Button` without styles and uses `PrimaryButton` as a replacement, it might break the UI due to unexpected styling.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1716018536609/6dffe870-6513-43bb-9ec7-d44c9802241d.png align="center")

### I - Interface Segregation Principle (ISP):

The I in SOLID stands for the Interface Segregation Principle. This principle emphasizes that instead of having large, monolithic interfaces(props), it's better to break them down into smaller, more specific interfaces. This allows for greater flexibility and reduces the burden on classes (components) that implement these interfaces.

React heavily utilizes components, and interfaces(props) define the way components can interact with each other. ISP emphasizes that avoid passing large prop objects to components. Instead, break down props into smaller, more specific ones that components can use independently. The example below shows how the Interface Segregation Principle can be applied in React:

A `ProductCard` component that displays product information and allows users to add the product to their cart. Initially, you define a single interface, `ProductProps`, that includes all functionalities:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1716890256667/42380948-6f5c-45d5-9d89-b0812f2e35e1.png align="center")

This interface serves the `ProductCard` component well. However, what if you want to reuse the product information display functionality (name, price, image) in another component, say a `ProductSearchList` component that displays a list of products? In this case, the `ProductSearchList` component doesn't need the `addToCart` functionality.

Following the Interface Segregation Principle, you can split the original `ProductProps` interface into two smaller ones:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1716890370166/1b49f82a-534f-4293-9a3b-7a945f4b92f5.png align="center")

Now, the `ProductCard` component can implement both `ProductInfoProps` and `AddToCartProps`, while the `ProductSearchList` component only needs to implement `ProductInfoProps`. This promotes better code reusability and reduces the dependencies between components.

Consider using ISP for components with multiple, distinct functionalities and potential for future growth.

### D - Dependency Inversion Principle (DIP):

Avoid tight coupling between components by using dependency injection, context, or props drilling to pass dependencies down the component tree. Favor abstractions and interfaces over concrete implementations to decouple components and make them easier to test and maintain.

An example of how the DIP in React is this: Imagine you have a `ProductCard` component that fetches product data from a hardcoded API endpoint within the component itself. This creates tight coupling between the component and the specific data source.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1716019285713/ab632cb3-7bbf-40f0-87b5-10223268db73.png align="center")

## Summary

While React may not be strictly object-oriented, the principles of SOLID can still guide you in writing cleaner, more maintainable React applications by encouraging modular, decoupled, and reusable component designs.

Though originally intended for object-oriented programming, the core concepts translate well to React's component-based structure.
