Maison  >  Article  >  interface Web  >  Principes SOLID dans React : la clé pour écrire des composants maintenables

Principes SOLID dans React : la clé pour écrire des composants maintenables

Susan Sarandon
Susan Sarandonoriginal
2024-09-29 06:19:29689parcourir

SOLID Principles in React: The Key to Writing Maintainable Components

À mesure que les applications React se développent, les choses peuvent rapidement devenir compliquées : composants gonflés, code difficile à maintenir et bugs inattendus. C’est là que les principes SOLID s’avèrent utiles. Développés à l'origine pour la programmation orientée objet, ces principes vous aident à écrire du code propre, flexible et évolutif. Dans cet article, je vais détailler chaque principe SOLID et montrer comment vous pouvez les utiliser dans React pour garder vos composants organisés, votre code plus facile à maintenir et votre application prête à se développer.

SOLID est un acronyme qui représente cinq principes de conception visant à écrire du code propre, maintenable et évolutif, à l'origine pour la programmation orientée objet mais également applicable dans React :

S : Principe de responsabilité unique : Les composants doivent avoir un seul travail ou une seule responsabilité.

O : Principe ouvert/fermé : les composants doivent être ouverts pour extension ** (facilement améliorés ou personnalisés) mais **fermés pour modification (leur code principal ne devrait pas avoir besoin changements).

L : Principe de substitution de Liskov : les composants doivent être remplaçables par leurs composants enfants sans perturber le comportement de l'application.

I : Principe de ségrégation des interfaces : Les composants ne doivent pas être forcés de dépendre de fonctionnalités inutilisées.

D : Principe d'inversion des dépendances : les composants doivent dépendre d'abstractions, et non d'implémentations concrètes.

Principe de responsabilité unique (SRP)

Pensez-y comme ceci : imaginez que vous avez un robot jouet qui ne peut faire qu'un seul travail, comme marcher. Si vous lui demandez de faire une deuxième chose, comme parler, il devient confus car il est censé se concentrer sur la marche ! Si vous voulez un autre travail, procurez-vous un deuxième robot.

Dans React, un composant ne doit faire qu'une seule chose. S'il en fait trop, comme récupérer des données, gérer les entrées de formulaire et afficher l'interface utilisateur en même temps, cela devient compliqué et difficile à gérer.

const UserCard = () => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch('/api/user')
      .then(response => response.json())
      .then(data => setUser(data));
  }, []);

  return user ? ( <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div> ) : <p>Loading...</p>;
};

Ici, la UserCard est responsable à la fois de la récupération des données et du rendu de l'interface utilisateur, ce qui enfreint le principe de responsabilité unique.

const useFetchUser = (fetchUser) => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUser().then(setUser);
  }, [fetchUser]);

  return user;
};

const UserCard = ({ fetchUser }) => {
  const user = useFetchUser(fetchUser);

  return user ? (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  ) : (
    <p>Loading...</p>
  );
};

Ici, la logique de récupération des données est déplacée vers un hook personnalisé (useFetchUser), tandis que UserCard se concentre uniquement sur le rendu de l'interface utilisateur et maintenir le SRP.

Principe ouvert/fermé (OCP)

Pensez à un personnage de jeu vidéo. Vous pouvez ajouter de nouvelles compétences au personnage (extensions) sans modifier ses capacités principales (modifications). C’est la raison d’être d’OCP : permettre à votre code de croître et de s’adapter sans altérer ce qui existe déjà.

const Alert = ({ type, message }) => {
  if (type === 'success') {
    return <div className="alert-success">{message}</div>;
  }
  if (type === 'error') {
    return <div className="alert-error">{message}</div>;
  }
  return <div>{message}</div>;
};

Ici, chaque fois que vous avez besoin d'un nouveau type d'alerte, vous devez modifier le composant Alert, ce qui interrompt l'OCP. chaque fois que vous ajoutez un rendu conditionnel ou changez de rendu de casse dans votre composant, vous rendez ce composant moins maintenable, car vous devez ajouter plus de conditions dans la fonctionnalité et modifier le code principal de ce composant qui rompt l'OCP.

const Alert = ({ className, message }) => (
  <div className={className}>{message}</div>
);

const SuccessAlert = ({ message }) => (
  <Alert className="alert-success" message={message} />
);

const ErrorAlert = ({ message }) => (
  <Alert className="alert-error" message={message} />
);

Maintenant, le composant Alert est ouvert pour extension (en ajoutant SuccessAlert, ErrorAlert, etc.) mais fermé pour modification car nous n'avons pas besoin de toucher au composant Alert principal pour ajouter de nouveaux types d'alertes.

Envie d'OCP ? Préférez la composition à l'héritage

Principe de substitution de Liskov (LSP)

Imaginez que vous avez un téléphone, puis que vous recevez un nouveau smartphone. Vous vous attendez à passer des appels sur votre smartphone comme vous le faisiez avec un téléphone ordinaire. Si le smartphone ne pouvait pas passer d’appels, ce serait un mauvais remplacement, non ? C'est l'essence même de LSP : les composants nouveaux ou enfants doivent fonctionner comme l'original sans casser les choses.

const Button = ({ onClick, children }) => (
  <button onClick={onClick}>{children}</button>
);

const IconButton = ({ onClick, icon }) => (
  <Button onClick={onClick}>
    <i className={icon} />
  </Button>
);

Ici, si vous échangez le Button avec l'IconButton, vous perdez l'étiquette, brisant le comportement et les attentes.

const Button = ({ onClick, children }) => (
  <button onClick={onClick}>{children}</button>
);

const IconButton = ({ onClick, icon, label }) => (
  <Button onClick={onClick}>
    <i className={icon} /> {label}
  </Button>
);

// IconButton now behaves like Button, supporting both icon and label

Maintenant, IconButton étend correctement le comportement de Button, prenant en charge à la fois les icônes et les étiquettes, afin que vous pouvez les échanger sans interrompre la fonctionnalité. Cela suit le principe de substitution de Liskov car l'enfant (IconButton) peut remplacer le parent (Button) sans aucune surprise !

Si le composant B étend le composant A, partout où vous utilisez le composant A, vous devriez pouvoir utiliser le composant B.

Principe de ségrégation d'interface (ISP)

Imaginez que vous utilisez une télécommande pour regarder la télévision. Vous n’avez besoin que de quelques boutons comme l’alimentation, le volume et la chaîne. Si la télécommande avait des tonnes de boutons inutiles pour un lecteur DVD, une radio et des lumières, ce serait ennuyeux à utiliser.

Supposons que vous disposiez d'un composant de table de données qui nécessite de nombreux accessoires, même si le composant qui l'utilise n'en a pas tous besoin.

const DataTable = ({ data, sortable, filterable, exportable }) => (
  <div>
    {/* Table rendering */}
    {sortable && <button>Sort</button>}
    {filterable && <input placeholder="Filter" />}
    {exportable && <button>Export</button>}
  </div>
);

This component forces all consumers to think about sorting, filtering, and exporting—even if they only want a simple table.

You can split the functionality into smaller components based on what’s needed.

const DataTable = ({ data }) => (
  <div>
    {/* Table rendering */}
  </div>
);

const SortableTable = ({ data }) => (
  <div>
    <DataTable data={data} />
    <button>Sort</button>
  </div>
);

const FilterableTable = ({ data }) => (
  <div>
    <DataTable data={data} />
    <input placeholder="Filter" />
  </div>
);

Now, each table only includes the functionality that’s needed, and you’re not forcing unnecessary props everywhere. This follows ISP, where components only depend on the parts they need.

Dependency Inversion Principle (DIP)

Imagine you're building with LEGO blocks. You have a robot built with specific pieces. But what if you want to swap out its arms or legs? You shouldn't have to rebuild the whole thing—just swap out the parts. The Dependency Inversion Principle (DIP) is like this: your robot (high-level) doesn't depend on specific parts (low-level); it depends on pieces that you can change easily.

const UserComponent = () => {
  useEffect(() => {
    fetch('/api/user').then(...);
  }, []);
  return <div>...</div>;
};

This directly depends on fetch—you can’t swap it easily.

const UserComponent = ({ fetchUser }) => {
  useEffect(() => {
    fetchUser().then(...);
  }, [fetchUser]);
  return <div>...</div>;
};

Now, the fetchUser function is passed in, and you can easily swap it with another implementation (e.g., mock API, or another data source), keeping everything flexible and testable.

Final Thoughts

Understanding and applying SOLID principles in React can drastically improve the quality of your code. These principles—Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion—help you write components that are more modular, flexible, and easier to maintain. By breaking down responsibilities, keeping code extensible, and making sure each part of your app interacts in predictable ways, you can create React applications that scale more easily and are simpler to debug. In short, SOLID principles lead to cleaner and more maintainable codebases.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn