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
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