Single Responsibility Principle (SRP)
A component should have only one reason to change, meaning it should have only one job.
Example: User Profile Component
Do:
- Split responsibilities into smaller, functional components.
// UserProfile.js
const UserProfile = ({ user }) => {
return (
<div>
<UserAvatar user={user} />
<UserInfo user={user} />
</div>
);
};
// UserAvatar.js
const UserAvatar = ({ user }) => {
return <img src={user.avatarUrl} alt={`${user.name}'s avatar`} />;
};
// UserInfo.js
const UserInfo = ({ user }) => {
return (
<div>
<h1>{user.name}</h1>
<p>{user.bio}</p>
</div>
);
};
Don't:
- Combine display, data fetching, and business logic in one component.
// IncorrectUserProfile.js
const IncorrectUserProfile = ({ user }) => {
// Fetching data, handling business logic and displaying all in one
const handleEdit = () => {
console.log("Edit user");
};
return (
<div>
<img src={user.avatarUrl} alt={`${user.name}'s avatar`} />
<h1>{user.name}</h1>
<p>{user.bio}</p>
<button onClick={handleEdit}>Edit User</button>
</div>
);
};
Open/Closed Principle (OCP)
Software entities should be open for extension, but closed for modification.
Example: Themable Button
Do:
- Use props to extend component functionalities without modifying the original component.
// Button.js
const Button = ({ onClick, children, style }) => {
return (
<button onClick={onClick} style={style}>
{children}
</button>
);
};
// Usage
const PrimaryButton = (props) => {
const primaryStyle = { backgroundColor: 'blue', color: 'white' };
return <Button {...props} style={primaryStyle} />;
};
Don't:
- Modify the original component code to add new styles or behaviors directly.
// IncorrectButton.js
// Modifying the original Button component directly for a specific style
const Button = ({ onClick, children, primary }) => {
const style = primary ? { backgroundColor: 'blue', color: 'white' } : null;
return (
<button onClick={onClick} style={style}>
{children}
</button>
);
};
Liskov Substitution Principle (LSP)
Objects of a superclass shall be replaceable with objects of its subclasses without breaking the application.
Example: Basic Button and Icon Button
Do:
- Ensure subclass components can replace superclass components seamlessly.
// BasicButton.js
const BasicButton = ({ onClick, children }) => {
return <button onClick={onClick}>{children}</button>;
};
// IconButton.js
const IconButton = ({ onClick, icon, children }) => {
return (
<button onClick={onClick}>
<img src={icon} alt="icon" />
{children}
</button>
);
};
Don't:
- Introduce subclass-specific properties that break functionality when substituting.
// IncorrectIconButton.js
// This button expects an icon and does not handle the absence of one, breaking when used as a BasicButton
const IncorrectIconButton = ({ onClick, icon }) => {
if (!icon) {
throw new Error("Icon is required");
}
return (
<button onClick={onClick}>
<img src={icon} alt="icon" />
</button>
);
};
Interface Segregation Principle (ISP)
No client should be forced to depend on methods it does not use.
Example: Text Component
Do:
- Provide specific interfaces for different uses.
// Text.js
const Text = ({ type, children }) => {
switch (type) {
case 'header':
return <h1>{children}</h1>;
case 'title':
return <h2>{children}</h2>;
default:
return <p>{children}</p>;
}
};
Don't:
- Clutter a component with unnecessary properties.
// IncorrectText.js
// This component expects multiple unrelated props, cluttering the interface
const IncorrectText = ({ type, children, onClick, isLoggedIn }) => {
if (isLoggedIn && onClick) {
return <a href="#" onClick={onClick}>{children}</a>;
}
return type === 'header' ? <h1>{children}</h1> : <p>{children}</p>;
};
Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Example: Data Fetching
Do:
- Use hooks or similar patterns to abstract data fetching
and state management.
// useUserData.js (Abstraction)
const useUserData = (userId) => {
const [user, setUser] = useState(null);
useEffect(() => {
fetchData(userId).then(setUser);
}, [userId]);
return user;
};
// UserProfile.js
const UserProfile = ({ userId }) => {
const user = useUserData(userId);
if (!user) return <p>Loading...</p>;
return <div><h1>{user.name}</h1></div>;
};
Don't:
- Hard-code data fetching inside components.
// IncorrectUserProfile.js
const IncorrectUserProfile = ({ userId }) => {
const [user, setUser] = useState(null);
useEffect(() => {
// Fetching data directly inside the component
fetch(`https://api.example.com/users/${userId}`)
.then(response => response.json())
.then(setUser);
}, [userId]);
if (!user) return <p>Loading...</p>;
return <div><h1>{user.name}</h1></div>;
};
Top comments (34)
As someone who came from a SOLID background (PHP) and started functional programming using React this article did an awesome job without trying to shoehorn JavaScript classes into React's functional style.
Love the examples of do vs don't. They were easy to follow, simple, and made sense. Good job
Hi Kearney, thanks so much for your kind words! Iβm really glad to hear that the examples were clear and helpful for you. If there's any other topic you'd like to see covered in this style, let me know!
Looking at this principles, I can boldly say that I have learnt the conventional ways of writing codes from Open University of Helsinki. Although, I never knew they had names but we were taught all these.
Thanks for this piece of information π
Thatβs fantastic, Monsur! It's great to hear about your experiences with the Open University of Helsinki.
Pretty nice article! ππ I created one regarding unit test and the component used as example was created using both SRP and OCP principles. I even said that in my article too.
Unit Testing: A Hands-On Guide with Real Examples - React + Vitest (p.1)
Amazing stuff!
Thank you very much. This help me so much!
You're welcome.
sensational
hehe
I really loved your article, I myself like the idea of applying SOLID in ReactJS and I have an article about it, congratulations on the great initiative and sharing of knowledge <3
Thank you so much for your kind words and for sharing your enthusiasm for applying SOLID principles in ReactJS.
Good explanation and example code.
Thank you!
I'm glad you found the information helpful!
Great examples
Thank you, Otavio! I put a lot of thought into those examples, so Iβm really glad they resonated with you.
nice read
Thanks Visakh!
Awesome article
Thanks, Jitendra.