DEV Community

Cover image for SE SOLID Principles For React/ React Native
Nour Abdou
Nour Abdou

Posted on

SE SOLID Principles For React/ React Native

Clean Architecture = Debt-free software.

Table of content:

  • Introduction
  • SOLID principles
  • Conclusion
  • References

Introduction

The best practices and good software design principles emerge and conceptualize to avoid repeating mistakes amap, and SOLID is unquestionably one of the more influential ones.

The tell-tale signs of poor architecture

The design of many software applications begins as a vital image in the minds of its designers. At this stage it is clean and elegant.
However, there are four primary symptoms that tell us that our designs are rotting:

  • Rigidity; the tendency for software to be difficult to change.
  • Fragility; the tendency of the software to break in many places every time it is changed.
  • Immobility; the inability to reuse software from other projects or from parts of the same project.
  • Viscosity; comes in two forms: viscosity of the design (When the design preserving methods -for making a change- are harder to employ than the hacks), and viscosity of the environment (when the development environment is slow and inefficient).

Also, if our designs are failing due to the constant rain of changing requirements, it is our designs that are at fault. We must somehow find a way to make our designs resilient to such changes and protect them from rotting.

Background

The SOLID principles were first introduced by the famous Computer Scientist Robert J. Martin (a.k.a Uncle Bob) in his paper [1] in 2000. But the SOLID acronym was introduced later by Michael Feathers.

The SOLID acronym stands for:

  • Single Responsibility Principle
  • Open-Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

SOLID principles

So, as Dr. Milan Milanoviฤ‡ Mentioned in a post ๐—›๐—ผ๐˜„ ๐—ง๐—ผ ๐—•๐—ฒ๐—ฐ๐—ผ๐—บ๐—ฒ ๐—” ๐—š๐—ฟ๐—ฒ๐—ฎ๐˜ ๐—ฆ๐—ผ๐—ณ๐˜๐˜„๐—ฎ๐—ฟ๐—ฒ ๐—˜๐—ป๐—ด๐—ถ๐—ป๐—ฒ๐—ฒ๐—ฟ?, you should learn ๐—ฆ๐—ผ๐—ณ๐˜๐˜„๐—ฎ๐—ฟ๐—ฒ ๐—˜๐—ป๐—ด๐—ถ๐—ป๐—ฒ๐—ฒ๐—ฟ๐—ถ๐—ป๐—ด concepts, such as: Software architecture, Design patterns & SOLID, etc.
And HERE WE GO... ๐Ÿ˜

1. What is the Single Responsibility Principle (SRP)?

There should never be more than one reason for a class to change.

This means that an entity (function/module/component) should do only one thing, changes should not impact the overall system.

โš› In React world:

Every component should solve a specific problem!
To ensure that our components are small and single purpose, we can:

  • Separating Data Processing Logic From the Code

One of the possible strategies is to create a custom hook for data fetching and filtering logic outside the component.

  • Separate the Code of Data Fetching to Make it Reusable

like making two separate hooks for Fetching data & Filtering data.

  • Decompose UI Components

Let's say we have a profile screen contains the following sections:
Main info, Settings, Details, etc.
Here, separate the code of the screen into different components for each section.

So, the same component should NOT holds state, fetches data, and renders the result. These are more responsibilities than it should have, and it will be very fragile after multiple changes.


2. What is the Open-Closed Principle (OCP)?

Software entities should be open for extension, but closed for modification.

In other words, it means the ability to add new functionality without touching the existing code for the entity to prevent the risk of creating potential bugs.

โš› In React world:

Itโ€™s related to Kent C. Doddsโ€™s โ€œapropcalypseโ€:

Unfortunately, truly maintainable, flexible, simple, and reusable components require more thought than: โ€œI need it to do this differently, so Iโ€™ll accept a new prop for that.โ€

Seasoned React developers know this leads to nothing but pain and frustration for both the maintainer and user of the component.

Assume that you have an Input component, for now its props are: the label text, value & onChange func. These props can grow into large set of props, such as: icons, multiple custom labes, custom styles, focus & blur funcs, help & error texts, and so on.

So the right way to start an extensible component is to use composition from the beginning; separate components for Input container, Input box, Label, Icons, Helping/error text, etc.

<InputContainer ...props>
    <InputLabel ...props />
    <InputPreAddon ...props />
    <Input ...props />
    <InputPostAddon ...props />
</InputContainer>
Enter fullscreen mode Exit fullscreen mode

This way, the Input component would be closed for modifications (no need for adding too much extra props) and open for extension (adding more components on need)


3. What is the Liskov Substitution Principle (LSP)?

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

A parent should be substitutable for its child without any problem, in order to strengthen consistency so that parent and children can be used in the same way without any errors.

โš› In React world:

The main takeaway from this principle is to use TypeScript. You can easily swap components if they share the same contract; like when two components use the same exact Props Interface.
Whenever a component uses another component, it shouldnโ€™t break its functionality (or create any surprises), here's how:
when a component has "string" prop, but you pass it as an object! obviously the code is perfectly fine without compilation error, BUT the application is crashing!

Now we'll see the importance of using TypeScript; If you want to pass anything thatโ€™s not a valid component to render (like rendering object on Text component), we will get a Type error. ๐Ÿ˜‰

And now, component that want to use another component need to follow its contract so that they donโ€™t create any unexpected behavior.


4. What is the Interface Segregation Principle (ISP)?

A client should never be forced to implement an interface that it doesnโ€™t use, or clients shouldnโ€™t be forced to depend on methods they do not use.

Classes should not be forced to implement a function they do no need in order to reduce unexpected bugs when the Class can't execute an action

โš› In React world:

To remove unnecessary code from a component, We split actions into smaller sets so it only perform the actions it requires.

In other words, We need to pass only the relevant information to the children components.

if you have a user object contains personal info & social accounts

const user = {
  name : "user name",
  age : "60",
  address : "user address",
  facebook : "Facebook user", 
  linkedIn : "linkedIn user"
}
Enter fullscreen mode Exit fullscreen mode

However, it uses two components: PersonalDetails & SocialAccounts to show the details

return <>
        <PersonalDetails user={user} />
        <SocialAccounts user={user} />
 </>
Enter fullscreen mode Exit fullscreen mode

So the PersonalDetails component doesn't need socials and the SocialAccounts component doesn't need personal details to function so itโ€™s clearly violating the Interface Segregation Principle.

To solve the issue, you can break down user object with new keys and pass the appropriate parts to the respective components only

const user ={

  personalDetails : {
     name : "user name",
     age : "60",
     address : "user address"
  },
  socialAcounts : {
     facebook : "Facebook user", 
     linkedIn : "linkedIn user"
  }
}
Enter fullscreen mode Exit fullscreen mode

5. What is the Dependency Inversion Principle(DIP)?

Depend upon abstractions, [not] concretions

The principle states:

  • High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces).
  • Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.

We want our classes to be open to extension (OCP), so we have reorganized our dependencies to depend on interfaces instead of concrete classes.

โš› In React world:

One Component shouldnโ€™t directly depend on another Component, but rather they both should depend on some common abstraction.
OR
No component or function should care about how a particular thing is done

Let's say you would want your app to make use of a payment gateway like Stripe.
You should depend on an Interface Payment component (which does NOT care about how online payment done) instead of concrete Stripe Payment component, so you can switch easily to another gateway in the future.


Conclusion

In Software development, the SOLID principle works as a guideline for developers.

In this article, weโ€™ve seen how by having some flexibility with interpretations of SOLID principles, we managed to apply them to our React Flow & Logics and make it more clean, maintainable and robust.

You should keep these principles in mind. But itโ€™s important to remember, that you as a software developer should draw your own conclusions based on your own research.
Thus, make sure you understand their definitions and what they do, then if you think they could be beneficial to your projects, try the methodology!

Frameworks are temporary but concepts are permanent

Hope SOLID principles are clear to you now. ๐Ÿ˜


References

  1. Robert C. Martin, Design Principles and Design Patterns. Available from: here
  2. YiฤŸit Kemal Erinรง, The SOLID Principles of Object-Oriented Programming Explained in Plain English [Internet]. freecodecamp. Available from: here
  3. Joel Olawanle, SOLID Principles for Programming and Software Design [Internet]. freecodecamp. Available from: here
  4. Josuรฉ Rodrรญguez, SOLID Principles Series. dev. Available from: here
  5. Anuupadhyay, How To Use Single Responsibility Principle in ReactJS?. geeksforgeeks. Available from: here
  6. Eduardo Moniz, Applying SOLID to react. medium. Available from: here
  7. Mohammad Faisal, How to Apply Interface Segregation Principle in ReactJS. medium. Available from: here

Top comments (0)