DEV Community

Cover image for Mastering S.O.L.I.D Principles in React: Easy Examples and Best Practices
drruvari
drruvari

Posted on

Mastering S.O.L.I.D Principles in React: Easy Examples and Best Practices

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

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} />;
};
Enter fullscreen mode Exit fullscreen mode

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

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>;
  }
};
Enter fullscreen mode Exit fullscreen mode

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>;
};
Enter fullscreen mode Exit fullscreen mode

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>;
};
Enter fullscreen mode Exit fullscreen mode

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>;
};
Enter fullscreen mode Exit fullscreen mode

Top comments (34)

Collapse
 
gitkearney profile image
Kearney Taaffe

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

Collapse
 
drruvari profile image
drruvari

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!

Collapse
 
abi0l9 profile image
Monsur Oyedeji

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 🙂

Collapse
 
drruvari profile image
drruvari

That’s fantastic, Monsur! It's great to hear about your experiences with the Open University of Helsinki.

Collapse
 
tassiofront profile image
Tássio

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)

Collapse
 
drruvari profile image
drruvari

Amazing stuff!

Collapse
 
fatjona profile image
fatjona

Thank you very much. This help me so much!

Collapse
 
drruvari profile image
drruvari

You're welcome.

Collapse
 
hasanbisha profile image
hasanbisha

sensational

Collapse
 
drruvari profile image
drruvari

hehe

Collapse
 
robertheory profile image
Roberto Costa

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

Collapse
 
drruvari profile image
drruvari

Thank you so much for your kind words and for sharing your enthusiasm for applying SOLID principles in ReactJS.

Collapse
 
kibaekkimm profile image
Kibaek Kim

Good explanation and example code.
Thank you!

Collapse
 
drruvari profile image
drruvari

I'm glad you found the information helpful!

Collapse
 
otarampinelli profile image
Otavio Rampinelli

Great examples

Collapse
 
drruvari profile image
drruvari

Thank you, Otavio! I put a lot of thought into those examples, so I’m really glad they resonated with you.

Collapse
 
vjnvisakh profile image
Visakh Vijayan

nice read

Collapse
 
drruvari profile image
drruvari

Thanks Visakh!

Collapse
 
jitendrachoudhary profile image
Jitendra Choudhary

Awesome article

Collapse
 
drruvari profile image
drruvari

Thanks, Jitendra.

Collapse
 
dugu007 profile image
dpd007

It's very helpful. Thanks👍🏻

Collapse
 
drruvari profile image
drruvari

You're welcome! I’m so glad you found the article helpful.

Collapse
 
andersonlimahw profile image
Anderson Lima

Amazing post!!

Collapse
 
drruvari profile image
drruvari

Thanks, Anderson! I appreciate your feedback.

Collapse
 
armandomuco profile image
armandomuco

that is awesome!

Collapse
 
drruvari profile image
drruvari

thank you!!

Collapse
 
arian94 profile image
Arian94

In ISP, the Text Component should extend let's say the H1 tag; ie. ComponentWithoutProps<'h1'>.

This will not force you to use unnecessary properties and it also follows the rule of OCP.

Collapse
 
drruvari profile image
drruvari

Hi Arian, that's an interesting take on ISP! Extending the H1 tag like that can indeed simplify component interfaces. Thanks for sharing this tip!

Collapse
 
deepakhce profile image
deepak-hce

Simple explanation and very powerful !!

Collapse
 
drruvari profile image
drruvari

Thank you, Deepak! I'm glad you found the explanation simple yet impactful. It's always great to hear that the content is hitting the right notes.