How to Apply Interface Segregation Principle in ReactJS

mohammadfaisal

Mohammad Faisal

Posted on July 4, 2023

How to Apply Interface Segregation Principle in ReactJS

To read more articles like this, visit my blog

SOLID is a set of principles that are not specific to any framework or language. These principles help us to understand how to write beautiful applications for our customers.

Today we will talk about the fourth principle of SOLID:

I: Interface Segregation Principle

We will try to understand the underlying concept of this principle and implement it in the context of ReactJS.

Previous Articles in this Series

  1. Single Responsibility Principle

  2. Open Closed Principle

  3. Liskov Substitution Principle

  4. Dependency Inversion Principle

What’s This Principle All About?

According to Stackify, the Interface Segregation Principle says

Clients should not be forced to depend upon interfaces that they do not use

In ReactJS we don’t use any interface, at least not in the sense of object-oriented programming. So the main takeaway for this scenario is:

Components should not depend on things they don’t need.

Now, let’s see how this principle can help us to write clean and beautiful ReactJS components.

A Practical Approach

Let’s say we have a User component that’s responsible for displaying the details of a user. Our user object looks something like this:

const user = {
  name : "Some user",
  age : "60",
  adderess : "House Address",
  bankName : "Some Bank", 
  bankAccountNumber: "1234567890"
}
Enter fullscreen mode Exit fullscreen mode

However, it uses two children components, named PersonalDetails and BankingDetails to show the details.

Our User component looks something like this:

import React from "react";

const user ={
  // described as before
}

export const User= () => {

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

Similarly our PersonalDetails looks like this:

import React from "react";

export const PersonalDetails= ({user}) => {

    return <>
        <div>name: {user.name}</div>
        <div>name: {user.age}</div>
        <div>name: {user.address}</div>
    </>
}
Enter fullscreen mode Exit fullscreen mode

And our BankingDetails.js looks like this:

import React from "react";

export const BankingDetails= ({user}) => {

    return <>
        <div>name: {user.bankName}</div>
        <div>name: {user.bankAccountNumber}</div>
    </>
}
Enter fullscreen mode Exit fullscreen mode

So, what’s the problem with this approach? Well, two things.

Firstly, our PersonalDetails component doesn't need banking information and our BankingDetails component doesn't need personal details to function so it’s clearly violating the Interface Segregation Principle.

Secondly, In the future, if we want to add typescript to our project (which you should) then to test PersonalDetails you’ll be required to mock the whole user object, though the banking information has nothing to do with PersonalDetails.

So, we need to fix this. But how?

Let’s Fix This

There are several approaches for fixing this problem, but the underlying principle is the same.

We need to pass only the relevant information to the children components.

So we will break down our data object and pass the appropriate parts to the respective components only.

Image Credit: [https://medium.com/@learnstuff.io/interface-segregation-principle-dd885e59aec9](https://medium.com/@learnstuff.io/interface-segregation-principle-dd885e59aec9)

Something like this:

import React from "react";

const user ={

  personalDetails : {
    name: "Some Name",
    age: "60",
    address: "Some address"
  },

  bankingDetails : {
    bankName: "Bank Name",
    bankAccountNumber: "1234567809"
  }
}

export const User= () => {

    return <>
        <PersonalDetails user={user.personalDetails} />
        <BankingDetails user={user.bankingDetails} />
    </>
}
Enter fullscreen mode Exit fullscreen mode

We’ve broken our user data into two parts with new keys personalDetails and bankingDetails and we passed this specific piece of data to our child components.

The Second Approach

The previous solution is perfect, but what if you don’t have control over the data? Perhaps it’s fetched from a remote source. Or what if you don’t want to modify the data structure for some reason?

Don’t worry. We can apply another technique to solve this:

import React from "react";

const user ={
  name: "Some Name",
  age: "60",
  address: "Some address",
  bankName: "Bank Name",
  bankAccountNumber: "1234567809"
}

export const User= () => {

    return <>
        <PersonalDetails name={user.name} age={user.age} address={user.address} />
        <BankingDetails bankName={user.bankName} bankAccountNumber={user.bankAccountNumber} />
    </>
}
Enter fullscreen mode Exit fullscreen mode

In our child components, we can use this as the following:

import React from "react";

export const PersonalDetails= ({name , age , address}) => {

    return <>
        <div>name: {name}</div>
        <div>name: {age}</div>
        <div>name: {address}</div>
    </>
}
Enter fullscreen mode Exit fullscreen mode

And:

import React from "react";

export const BankingDetails= ({bankName , bankAccountNumber}) => {

    return <>
        <div>bankName: {bankName}</div>
        <div>bankAccountNumber: {bankAccountNumber}</div>
    </>
}
Enter fullscreen mode Exit fullscreen mode

Now our child components only get what they need. They are cleaner and easier to understand.

Now our components don't depend on anything that they don’t need.

Final Nail in The Coffin

Up to this point, we introduced the concept of the Interface Segregation Principle to make our components tight. But in our previous article on Liskov Substitution Principle, we saw that our current solution has a problem and that is anything can be passed onto the children component.

Let’s introduce typescript in our project and solve that issue.

import React from "react";

interface IPersonalDetails {
  name: string;
  age: number;
  address: string
}

export const PersonalDetails= ({name , age , address}: IPersonalDetails) => {

    return <>
        <div>name: {name}</div>
        <div>name: {age}</div>
        <div>name: {address}</div>
    </>
}
Enter fullscreen mode Exit fullscreen mode

Similarly, we can do the same to BankingDetails component.

import React from "react";

interface IBankingDetails{
  bankName: string;
  bankAccountNumber: string;
}

export const BankingDetails= ({bankName , bankAccountNumber}: IBankingDetails) => {

    return <>
        <div>bankName: {bankName}</div>
        <div>bankAccountNumber: {bankAccountNumber}</div>
    </>
}
Enter fullscreen mode Exit fullscreen mode

Now we are forced to pass only the relevant data to the children components. No more unwanted bugs for you!

How great is that? :D

Final Thoughts

These are just principles to guide your way of thinking — not hard and fast rules to build your application.

Knowledge of these concepts will take you ahead of others. These concepts will surely help you to understand the core principles of programming.

Because frameworks are temporary but concepts are permanent

I hope you enjoyed this article as much I enjoyed writing it. Have a Great Day!

Resources

Have something to say? Get in touch with me via LinkedIn or Personal Website

💖 💪 🙅 🚩
mohammadfaisal
Mohammad Faisal

Posted on July 4, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related