Interface Segregation Principle in React
Zahid Hasan
Posted on January 21, 2023
What is the Single Responsibility Principle?
The Interface Segregation Principle (ISP) is an oop principle that states that clients (objects or classes) should not be required to implement interfaces (or methods) that they do not utilize. In other words, it is a philosophy that favors smaller, more particular interfaces over larger, more broad ones. This contributes to reducing system complexity by making it more modular and easier to comprehend and maintain.
Consider the Toy interface, which offers numerous methods such as play(), stop(), and break() (). Assume we have two kinds of toys: a vehicle toy and a doll toy. The play() and stop() methods are sufficient for the vehicle toy, while the play() and break() methods are required for the doll toy.
If we do not adhere to the ISP, the Toy interface would have all three methods (play(), stop(), and break()), and both the vehicle toy and the doll toy would be required to implement all three methods, even though the doll toy will never utilize the stop() method.
We would construct two new interfaces, VeichleToy and DollToy, in accordance with the ISP. The VeichleToy interface would only have the play() and stop() methods, whereas the DollToy interface would only have the break() and play() methods. In this manner, the vehicle toy can implement the VeichleToy interface and the doll toy can implement the DollToy interface without implementing extra methods.
We have thereby decreased the system’s complexity while increasing its maintainability.
How can you implement ISP in React?
In React, you can implement the Interface Segregation Principle using hooks or Higher-Order Components (HOC), or Render Props.
In this article, we will use hooks to learn about this.
Here’s an example of how the ISP can be violated when using React hooks:
Assume you have a UserProfile
component that displays a user’s information such as their name, email, and profile image. The component utilizes the useEffect
hook to contact an API and set the user’s data in the component’s state in order to retrieve the user’s data.
Suppose you have a component named UserSettings
that similarly needs to get the user’s data, but this time it needs to update the user’s data by accessing another API.
You might be tempted to reuse the UserProfile
component’s code to retrieve the user’s data in this scenario. However, you are breaking the ISP by doing so because the UserProfile
component simply needs to fetch data, whereas the UserSettings
component needs to fetch and alter data.
Here’s the code that violates the ISP:
import { useEffect, useState } from 'react';
const UserProfile = () => {
const [userData, setUserData] = useState({});
useEffect(() => {
const fetchUserData = async () => {
const response = await fetch('/api/user');
const data = await response.json();
setUserData(data);
}
fetchUserData();
}, []);
return <div>{userData.name}</div>;
}
const UserSettings = () => {
const [userData, setUserData] = useState({});
useEffect(() => {
const fetchUserData = async () => {
const response = await fetch('/api/user');
const data = await response.json();
setUserData(data);
}
fetchUserData();
}, []);
const updateUserData = async () => {
await fetch('/api/user', {
method: 'PATCH',
body: JSON.stringify(userData),
});
}
return (
<div>
<input
value={userData.name}
onChange={(e) => setUserData({ ...userData, name: e.target.value })}
/>
<button onClick={updateUserData}>Save</button>
</div>
);
}
You may solve this issue by creating two different hooks: one for getting data and one for changing data. Then, in each component, you may use the relevant hook.
For example, you might construct a hook named useFetchUserData
that just deals with getting the user’s data and another called useUpdateUserData
that only deals with updating the user’s data. The useFetchUserData
hook may be used by the UserProfile
component, while the useUpdateUserData
hook can be used by the UserSettings
component.
Because each component only implements the methods it requires, and the hooks are short and have particular interfaces, you are complying with the ISP.
Here’s the refactored code:
import { useEffect, useState } from 'react';
const useFetchUserData = () => {
const [userData, setUserData] = useState({});
useEffect(() => {
const fetchData = async () => {
const response = await fetch('/api/user');
const data = await response.json();
setUserData(data);
}
fetchData();
}, []);
return userData;
}
const useUpdateUserData = () => {
const [userData, setUserData] = useState({});
const updateData = async () => {
await fetch('/api/user', {
method: 'PATCH',
body: JSON.stringify(userData),
});
}
return { userData, setUserData, updateData };
}
const UserProfile = () => {
const userData = useFetchUserData();
return <div>{userData.name}</div>;
}
const UserSettings = () => {
const { userData, setUserData, updateData } = useUpdateUserData();
return (
<div>
<input
value={userData.name}
onChange={(e) => setUserData({ ...userData, name: e.target.value })}
/>
<button onClick={updateData}>Save</button>
</div>
);
}
Posted on January 21, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.