Introduction

React JS, often simply referred to as React, is a widely-used open-source JavaScript library for building user interfaces. Developed and maintained by Facebook, React JS has gained immense popularity among developers for its efficiency and flexibility in creating dynamic and interactive web applications. What sets React JS apart is its component-based architecture, enabling developers to break down complex UIs into reusable and manageable building blocks. By employing a virtual DOM (Document Object Model), React JS optimizes performance by efficiently updating only the necessary parts of a page, resulting in smoother user experiences. Whether you’re building single-page applications or integrating components into larger projects, React JS declarative syntax and comprehensive ecosystem make it a go-to choice for modern front-end development.

React JS – SOLID Principles

In the realm of software engineering, SOLID is an acronym comprising five fundamental design principles. (Please note that the code samples included are solely intended to illustrate the implementations).

The principles are as follows:

  • Single-responsibility principle
  • Open–closed principle
  • Liskov substitution principle
  • Interface segregation principle
  • Dependency inversion principle

SOLID principles aim to enhance object-oriented designs, making them more understandable, flexible, and maintainable.

In this article, we will explore each principle with practical examples to provide a clear and simplified understanding of their significance and application.

Single-responsibility principle (SRP)

  • “There should never be more than one reason for a class to change.” This implies that each class should possess only one responsibility.
  • Put simply, it advocates that a class should have a single, specific task or responsibility. Any changes related to that task should remain within that class and not be scattered across multiple locations.

Consider the code which lists the cart items, which list the cart items and handles the UI as well, which has two responsibilities.

React JS

So if we want to add the SRP, the code can be changed as below, by separating the rendering part (cart.js) and listing part.

React Js

cart.js

React JS

Open–closed principle (OCP)

  • “Software entities … should be open for extension, but closed for modification.”
  • In simpler terms, it suggests that after writing and testing a piece of code, we should avoid direct modifications when adding new features or improvements. Instead, we should be able to extend its behaviour without altering its existing code.

Implementation: Initially, we will create a fundamental Product component responsible for presenting the information of an individual product.

React Js

Below is ProductList component tasked with exhibiting a collection of products.

React Js

Consider a scenario where we aim to introduce a fresh feature to emphasize products on sale. Instead of directly altering the current Product component, we can create a new component named ProductOnSale, which extends the capabilities of the original Product component.

React Js

Lastly, we can proceed with updating the ProductList component to employ ProductOnSale for products that are currently on sale, while retaining the use of the regular Product component for products with regular prices.

React JS

By adhering to the Open-Closed Principle, we have the ability to incorporate new features, such as the on-sale highlighting, without directly altering existing components. This approach enhances the maintainability of our code, facilitates easier extension, and reduces the likelihood of introducing bugs when implementing modifications.

Liskov substitution principle

  • “Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.”
  • In easier terms, if a class (or object) is a subclass of another class, it should seamlessly replace its superclass without triggering unexpected errors or behavior. This implies that the subclass should uphold the contract and behavior defined by its superclass.

Consider below code snippet

React JS
React JS

In this instance, we have a foundational component named SizeButton, which serves as a representation of an individual size selection button. Subsequently, two distinct components, ShirtSizeButton and ShoeSizeButton, extend the behavior of SizeButton by reusing its functionality while providing specific size values. The SizeButton component conforms to the Liskov Substitution Principle, as its subclasses (ShirtSizeButton and ShoeSizeButton) can replace it seamlessly without compromising the correctness of the product details page.

Both specific components (ShirtSizeButton and ShoeSizeButton) are interchangeable with the base component (SizeButton) and fulfill the contract of displaying a size selection button with the provided size, isSelected state, and onClick handler. This adherence to the contract ensures consistency and allows the components to be easily swapped in and out, enhancing flexibility and maintainability.

Interface segregation principle

  • “Clients should not be forced to depend upon interfaces that they do not use.”
  • In easier terms, it suggests that while designing interfaces (sets of methods that a class must implement), we should keep them small and focused on specific tasks. Each class should only implement the methods that are directly relevant to its own functionality.

Implementation: Consider the below code which displays the customer details

In the aforementioned example, we encounter a situation where we pass the complete customer details to the CustomerAddress component, despite all details not being required. This practice introduces risks and unnecessary complexity to the component, consequently violating the Interface Segregation Principle (ISP).

We can improve the above code to adhere to the Interface Segregation Principle (ISP) by refactoring it as shown below. In this example, the CustomerAddress component only accepts the required details, specifically the address, thereby mitigating risks and avoiding unnecessary dependencies.

React JS

Dependency inversion principle

  • “Depend upon abstractions, [not] concretions.”
  • Put plainly, it advises us to avoid coding classes to depend on specific details of other classes. Instead, we should design our code to depend on abstract interfaces. This approach enables us to change the implementation of those classes without impacting the high-level modules that utilize them.

Implementation: Consider the below code which renders the product image on the product detail page.

React JS

Product detail component

React JS

App.js

React JS

To achieve a certain degree of decoupling between the ProductDetailsPage and the specific implementation of product image rendering, we pass the actual ProductImage component as a prop to the ProductDetailsPage. This approach allows us to interchange ProductImage with an alternative component seamlessly, without the need to modify ProductDetailsPage. By doing so, we uphold the Dependency Inversion Principle in a more comprehensive manner.

I trust that the preceding article provides valuable insights into applying SOLID principles in React.js.

Conclusion

In conclusion, the journey of enhancing your React JS codebase by embracing SOLID principles is one that promises substantial rewards. By delving into each of the five key principles, we’ve navigated through the intricacies of building more robust and maintainable applications. The Single-responsibility principle has shown us the art of designing classes with a singular purpose, streamlining development and maintenance.

The Open–closed principle has taught us the elegance of extending functionality without disturbing existing code, leading to a more flexible and scalable architecture. Adhering to the Liskov substitution principle ensures seamless interchangeability of components, fostering consistency and adaptability.

The Interface segregation principle has emphasized the importance of focused and tailored interfaces, eliminating unnecessary dependencies.

Lastly, the Dependency inversion principle has transformed our approach to class dependencies, allowing for smoother changes and upgrades. By incorporating these SOLID principles into your React JS development arsenal, you’re not just writing code – you’re crafting a foundation for excellence and innovation in the dynamic world of front-end development.

LinkedIn

https://www.linkedin.com/pulse/elevating-your-react-codebase-embracing-solid-prakash-revanna/

React JS Dev

https://react.dev/

TechPanda Hub