Nous présentons à votre attention une traduction d'un article de Scott Domes, qui a été publié sur blog.bitsrc.io. Découvrez sous kat pourquoi les composants doivent être aussi petits que possible et comment le principe de la responsabilité exclusive affecte la qualité des applications.

Photo
Austin Kirk avec
UnsplashL'avantage du système de composants React (et des bibliothèques similaires) est que votre interface utilisateur est divisée en petites parties faciles à lire et réutilisables.
Ces composants sont compacts (100-200 lignes), ce qui permet aux autres développeurs de les comprendre et de les modifier facilement.
Bien que les composants, en règle générale, essaient d'être plus courts, il n'y a pas de restriction claire et stricte sur leur longueur. React ne vous dérangera pas si vous décidez d'intégrer votre application dans un composant effroyablement énorme, composé de 3000 lignes.
... mais ça n'en vaut pas la peine. La plupart de vos composants, probablement, sont déjà trop volumineux - ou plutôt, ils remplissent trop de fonctions.
Dans cet article, je prouverai que la plupart des composants (même avec la longueur habituelle de 200 lignes) devraient être ciblés plus étroitement. Ils ne devraient remplir qu'une seule fonction et l'exécuter correctement. C'est ce qu'Eddie Osmani dit grand
ici .
Astuce : lorsque vous travaillez dans JS,
utilisez Bit pour organiser, assembler et réutiliser des composants en tant que pièces lego. Bit est un outil extrêmement efficace pour cette entreprise, il vous aidera, vous et votre équipe, à gagner du temps et à accélérer l'assemblage. Essayez-le.
Montrons comment, lors de la création de composants
, quelque chose peut mal tourner .
Notre appli
Imaginez que nous ayons une application standard pour les blogueurs. Et voici ce que sur l'écran principal:
class Main extends React.Component { render() { return ( <div> <header> // Header JSX </header> <aside id="header"> // Sidebar JSX </aside> <div id="post-container"> {this.state.posts.map(post => { return ( <div className="post"> // Post JSX </div> ); })} </div> </div> ); } }
(Cet exemple, comme de nombreux autres, doit être considéré comme un pseudo-code.)Il affiche le panneau supérieur, la barre latérale et la liste des articles. Tout est simple.
Comme nous devons également télécharger des publications, nous pouvons le faire pendant le montage du composant:
class Main extends React.Component { state = { posts: [] }; componentDidMount() { this.loadPosts(); } loadPosts() {
Nous avons également une certaine logique par laquelle la barre latérale est appelée. Si l'utilisateur clique sur le bouton dans le panneau supérieur, le côté sortira. Vous pouvez le fermer à partir du haut et du panneau latéral lui-même.
class Main extends React.Component { state = { posts: [], isSidebarOpen: false }; componentDidMount() { this.loadPosts(); } loadPosts() {
Notre composant est devenu un peu plus compliqué, mais toujours facile à lire.
On peut affirmer que toutes ses parties ont un seul objectif: afficher la page principale de l'application. Nous suivons donc le principe de la responsabilité exclusive.
Le principe de la responsabilité exclusive stipule qu'un composant ne doit remplir qu'une seule fonction. Si nous reformulons la définition tirée de
wikipedia.org , il s'avère que chaque composant ne devrait être responsable que d'une partie de la fonctionnalité [application].
Notre composant principal répond à cette exigence. Quel est le problème?
Voici une formulation différente du principe:
tout [composant] ne devrait avoir qu'une seule raison de changer .
Cette définition est tirée du
livre de Robert Martin
, Rapid Software Development. Principes, exemples, pratique » et c'est d'une grande importance.
En nous concentrant sur
une raison de changer nos composants, nous pouvons créer de meilleures applications qui, de plus, seront faciles à configurer.
Pour plus de clarté, compliquons notre composant.
Complication
Supposons qu'un mois après la mise en œuvre du composant principal, une nouvelle fonctionnalité ait été attribuée au développeur de notre équipe. L'utilisateur pourra désormais masquer une publication (par exemple, si elle contient du contenu inapproprié).
Ce n'est pas difficile à faire!
class Main extends React.Component { state = { posts: [], isSidebarOpen: false, postsToHide: [] };
Notre collègue s'en est facilement occupé. Elle n'a ajouté qu'une nouvelle méthode et une nouvelle propriété. Aucun de ceux qui ont examiné la courte liste de changements n'a émis d'objection.
Quelques semaines plus tard, une autre fonctionnalité est annoncée - une barre latérale améliorée pour la version mobile. Au lieu de jouer avec CSS, le développeur décide de créer plusieurs composants JSX qui ne fonctionneront que sur les appareils mobiles.
class Main extends React.Component { state = { posts: [], isSidebarOpen: false, postsToHide: [], isMobileSidebarOpen: false };
Un autre petit changement. Quelques nouvelles méthodes bien nommées et une nouvelle propriété.
Et ici, nous avons un problème.
Main
n'exécute toujours qu'une seule fonction (rendre l'écran principal), mais vous regardez toutes ces méthodes que nous traitons maintenant:
class Main extends React.Component { state = { posts: [], isSidebarOpen: false, postsToHide: [], isMobileSidebarOpen: false }; componentDidMount() { this.loadPosts(); } loadPosts() {
Notre composant devient grand et volumineux, il est difficile à comprendre. Et avec l'extension des fonctionnalités, la situation ne fera qu'empirer.
Qu'est-ce qui a mal tourné?
Seule raison
Revenons à la définition du principe de la responsabilité exclusive:
tout composant ne devrait avoir qu'une seule raison de changer .
Auparavant, nous avons changé la façon dont les publications sont affichées, nous avons donc dû changer notre composant principal. Ensuite, nous avons changé la façon dont la barre latérale s'ouvre - et encore une fois, nous avons changé le composant principal.
Cette composante a de nombreuses raisons indépendantes de changement.
Cela signifie qu'il remplit trop de fonctions .
En d'autres termes, si vous pouvez modifier de manière significative une partie de votre composant et que cela n'entraîne pas de modifications dans une autre partie de celui-ci, alors votre composant a trop de responsabilités.
Séparation plus efficace
La solution est simple: vous devez diviser le composant principal en plusieurs parties. Comment faire
Commençons à nouveau. Le rendu de l'écran principal reste la responsabilité du composant principal, mais nous le réduisons uniquement pour afficher les composants associés:
class Main extends React.Component { render() { return ( <Layout> <PostList /> </Layout> ); } }
Super
Si nous changeons soudainement la disposition de l'écran principal (par exemple, ajoutons des sections supplémentaires), alors Main changera également. Dans d'autres cas, nous n'aurons aucune raison de le toucher. Super.
Passons à la
Layout
en
Layout
:
class Layout extends React.Component { render() { return ( <SidebarDisplay> {(isSidebarOpen, toggleSidebar) => ( <div> <Header openSidebar={toggleSidebar} /> <Sidebar isOpen={isSidebarOpen} close={toggleSidebar} /> </div> )} </SidebarDisplay> ); } }
C'est un peu plus compliqué.
Layout
est responsable du rendu des composants de mise en page (panneau latéral / panneau supérieur). Mais nous ne succomberons pas à la tentation et donnerons à la
Layout
responsabilité de déterminer si la barre latérale est ouverte ou non.
Nous
SidebarDisplay
cette fonction au composant
SidebarDisplay
, qui transmet les méthodes ou états nécessaires aux composants
Header
et
Sidebar
.
(L'exemple ci-dessus est un modèle Render Props via Children dans React. Si vous ne le connaissez pas, ne vous inquiétez pas. Il est important qu'il existe un composant distinct qui contrôle l'état ouvert / fermé de la barre latérale.)Et puis, la
Sidebar
elle-même peut être assez simple si elle est uniquement responsable du rendu de la barre latérale à droite.
class Sidebar extends React.Component { isMobile() {
Encore une fois, nous résistons à la tentation d'insérer JSX pour ordinateurs / appareils mobiles directement dans ce composant, car dans ce cas, il y aura deux raisons pour le changement.
Regardons un autre composant:
class PostList extends React.Component { state = { postsToHide: [] } filterPosts(posts) {
PostList
ne change que si nous modifions le rendu de la liste des articles. Semble évident, non? C'est exactement ce dont nous avons besoin.
PostLoader
ne change que si nous changeons la façon dont les messages sont chargés. Et enfin,
Post
ne change que si nous changeons la façon dont le post est rendu.
Conclusion
Tous ces composants sont minuscules et remplissent une petite fonction. Les raisons de ces modifications sont faciles à identifier et les composants eux-mêmes sont testés et corrigés.
Maintenant, notre application est beaucoup plus facile à modifier - réorganiser les composants, ajouter de nouvelles fonctionnalités et étendre les fonctionnalités existantes. Vous avez juste besoin de regarder un fichier de composant pour déterminer à
quoi il sert.
Nous savons que nos composants changeront et grandiront avec le temps, mais l'application de cette règle universelle vous aidera à éviter les dettes techniques et à augmenter la vitesse de l'équipe. La répartition des composants dépend de vous, mais n'oubliez pas - il ne
doit y
avoir qu'une seule raison pour changer le composant .
Merci de votre attention et attendons vos commentaires avec impatience!