Apply the Dependency Inversion Principle in React

The dependency inversion principle is one of the famous SOLID principles. Also, it is one of the most important ones.

Today, we will see how to solve a very common mistake that novice React developers make using this principle.

I will try to keep it very simple. Let’s get started!

What Does This Principle Tell Us?

In terms of object-oriented programming, the main idea behind this principle is to always have a high-level code interface with abstraction rather than an implementation detail.

Hold on! I know what you are thinking: “I am a simple frontend developer. Why are you bothering me with these complex terms?”

Let me state it simply for you. For a React application, this principle means: > “No component or function should care about how a particular thing is done.”

Still not clear? OK, let’s get our hands dirty with some code!

A Practical Example

Let’s take a very common use case. We are going to make an API call from our component to get some data from a remote source. An implementation can look like this:

import React from "react";

const REMOTE_URL = 'https://jsonplaceholder.typicode.com/users'

export const Users = () => {
  
    const [users , setUsers] = useState([])

    useEffect(() => {
      
      fetch(URL)
        .then(response => response.json())
        .then(json => setUsers(json))
        
    },[])

    return <>
        <div> Users List</div>
        {filteredUsers.map(user => <div>{user.name}</div>)}
    </>
}

Look at this component. It depends on some remote data that is fetched right inside the component.

Our Users component’s main responsibility is to render the data. It should not care about how data is fetched or where the data comes from.

This component knows too much — and that’s a problem.

Why?

Well, let’s say you have ten other components and all of them fetch their own data.

Now your manager comes along and tells you to use axios instead of fetch

You are in trouble! Now you have to go into each file and refactor the logic to use axios.

But life is not so simple! After a few days, your manager comes again and tells you to implement caching.

You have to do the same thing once again.

Thus, it increases the chance of introducing a bug in your software. Also, the code becomes unmaintainable and valuable time is wasted.

So What Should We Do Then?

Let’s introduce a data-fetching Hook and abstract away our logic outside our component because that’s exactly what this principle tells us. To depend on abstraction, remember?

import {useState} from "react";

export const useFetch = (URL) => {

    const [data , setData] = useState([])

    useEffect(() => {
      
        fetch(URL)
            .then(response => response.json())
            .then(json => setData(json))
            
    },[])

    return data;

}

Now use this Hook inside our Users component:

import React from "react";
import useFetch from './useFetch'

const REMOTE_URL = 'https://jsonplaceholder.typicode.com/users'

export const Users = () => {
  
    const users = useFetch(REMOTE_URL)

    return <>
        <div> Users List</div>
        {filteredUsers.map(user => <div>{user.name}</div>)}
    </>
}

Notice a great thing about this solution: Your useFetch Hook doesn’t care about who is calling it. It just takes a URL as an input and returns the data.

Now all other components can take advantage of the Hook that we just wrote. And our Users component no longer depends on the concrete details on how the data is coming back or which library is being used!

More Advanced Usage

Now let’s satisfy your manager with basic caching functionality:

import {useState} from "react";

export const useFetch = (URL) => {

    const [data , setData] = useState([])

    useEffect(() => {
      
      const cachedData = localstorage.getItem(URL)
      
      if(cachedData) {
        setData(JSON.parse(cachedData))
      }
      else{
        fetch(URL)
        .then(response => response.json())
        .then(json => setData(json))
      }
            
    },[URL])
    
    useEffect(() => {
      localstorage.setItem(URL , JSON.stringify(data))
    },[data])

    return users;

}

You have to change the code in only one place now. That’s great! Let’s say you need to show API errors as a toast. Can you do that now? If so, then you got my point.

How To Detect It

  • In most cases, if you are violating the single-responsibility principle, then you might also be violating the dependency inversion principle.

  • For any component, look into the import section at the top. If you are importing some library that’s not responsible for displaying something (e.g. a toast or modal), then you might be violating the principle.

Previous Articles in this Series

  1. Single Responsibility Principle

  2. Open Closed Principle

  3. Liskov Substitution Principle

  4. Interface Segregation Principle

That’s all for today. I hope you enjoyed this article as well as this series.

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


Share this post


Read more articles...

team

Document Your React Applications The Right Way

team

Download HTML as a PDF in React

team

How to Access Firebase From AWS Lambda

team

How to Setup and Add Google Analytics to your React App

Profile Image

Who I am

Hi, I amMohammad Faisal, A full-stack software engineer @Cruise , working remotely from a small but beautiful country named Bangladesh.

I am most experienced inReactJS,NodeJS andAWS

Buy Me a Coffee Widget