Ecrire du code plus propre pour React

“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” – Martin Fowler

Pourquoi prendre la peine et le temps d’écrire du « code clean » ?

Un code propre facilite la lecture, l’évolution et la maintenance de celui-ci. Cela passe par la mise en place de règles commune afin d’écrire du code que tout le monde comprend, accélérant ainsi le développement et réduisant les risques d’erreur.

Sur le long terme cela ne peut être que bénéfique pour vous, vos équipes et pour vos projets.  

Avant de commencer, si vous vous débutez avec React, je vous conseille de suivre le guide rédigé par Airbnb sur React/JSX (https://github.com/airbnb/javascript/tree/master/react). Ce guide vous apprendra la majorité des conventions et normes actuellement répandues sur le développement en React.

Nous pouvons maintenant attaquer !

Vous trouverez dans cet article, 11 bonnes pratiques de développement. Certaines de ses règles sont basiques et évidentes si vous avez quelques années d’expérience ; cependant j’aime à penser que « Common Sense is Not Common Practice ». De plus, quelqu’unes de ces bonnes pratiques ne sont pas spécifiques à React et peuvent être appliquées dans tous types de projets.  

1 – Utilisation d’un Linter :

Un Linter est un outil qui analyse statiquement du code et vérifie que celui-ci respecte un certain nombre de règles.

L’intérêt est multiple :

  • Vous êtes assuré de la constance du code, qu’il s’agisse de bonnes pratiques ou de considérations plus esthétiques : autant de points plus ou moins triviaux dont vous n’aurez plus à vous soucier directement.
  • Vous êtes toujours à jour sans avoir à faire d’effort, les mises à jour du Linter prenant en considération les évolutions des bonnes pratiques de développement.
  • En cas d’erreur de syntaxe dans votre code, l’analyse statique de ce dernier échouera, et l’erreur en question vous sera remontée : c’est un garde-fou supplémentaire

Cet outil est très simple à mettre en place et assure une bonne qualité de votre code. Un outil indispensable ! 

EsLint (https://eslint.org/), TsLint (https://palantir.github.io/tslint/)

2 – Un code propre devrait se comprendre sans commentaire :

Il est parfois nécessaire d’expliquer du code complexe par un commentaire, afin de faciliter la vie du collègue qui passera après nous.

Cependant l’excès de commentaire peut avoir l’effet totalement inverse. Premièrement, cela va diminuer la visibilité du code, rendant la lecture de celui-ci plus compliqué et plus longue.

Mais surtout cela peut introduire de l’incompréhension et entrainer une perte de temps importante. En effet, si les commentaires n’ont pas évolué pour refléter les modifications du code, lors de correction de bug ou l’implémentation d’évolution, ils deviennent contre-productifs, apportant des explications qui ne sont plus vraies.

Pour éviter ce problème, il est nécessaire de limiter au maximum le nombre de commentaires. Et il est préférable de mettre en place d’autres règles comme une convention de nommage.

3 – Convention de nommage:

Il est important d’établir avec son équipe une convention de nommage et de la respecter. Rédiger ensemble des règles sur le nommage des fonctions, des noms des variables permet une homogénéisation du code ; ce qui permet à toute l’équipe de gagner en rapidité de lecture et faciliter la compréhension du code dans sa globalité.

Voici quelques règles communes (cette liste n’est pas exhaustive):

  • Les variables du type Boolean et les fonctions retournant un Boolean doivent commencer par ‘is’,’has’ ou ‘should’ exemple :
  • Utilisez l’extension .jsx pour les composants React. (.tsx dans un contexte TypeScript)
  • Utilisez PascalCase pour les noms de fichiers (par exemple, LoginComponent.jsx).
  • Utilisez le nom de fichier comme nom de composant.
  • Les événements React sont nommés à l’aide de camelCase et commencent par ‘on’ (par exemple, onSubmitButtonClick).
  • Les gestionnaires d’événements doivent commencer par ‘handle’ (par exemple, handleSubmitButtonClicked).
  • Les fonctions doivent être nommées pour ce qu’elles font, pas comment elles le font. Parce que la façon dont vous réalisez le traitement peut évoluer au cours du temps, et vous ne devriez pas avoir besoin de modifier toutes les références de ces fonctions dans votre code à cause de cela.

4- Un code propre est ‘DRY’

DRY est un acronyme qui signifie «Don’t Repeat Yourself». Si vous faites la même chose à plusieurs endroits, consolidez votre code et supprimez les duplications.

La factorisation de votre code est très importante, car elle améliore grandement la maintenabilité de celui-ci. En effet la duplication peut poser de sérieux problèmes pour le projet, car celles-ci rendent les futures évolutions plus risquées et peuvent causer des bugs très difficiles à identifier. Le plus grand danger étant que le code dupliqué évolue de façon inconsistante.

Attention : Sachez qu’il est possible d’aller trop loin dans la factorisation de votre code. Rendant, celui-ci inutilement complexe afin de prendre en compte tous les cas possibles, ce qui va alors à l’encontre de l’intention initiale. 

5 – Diviser pour mieux régner :

Utilisez des petites fonctions, chacune ayant un rôle très précis. C’est ce qu’on appelle le principe de responsabilité unique. Assurez-vous que chaque fonction fait son travail et le fait bien.

La division de vos fonctions les plus importantes en plusieurs fonctions plus petites permettra leurs réutilisations et deviendra également beaucoup plus facile à tester.

6 – Utiliser le package classnames :

classnames est un excellent package pour générer des noms de classes de composants. En pratique, il existe de nombreux cas où différents styles doivent être appliqués à même composant. Pour éviter l’abondance de conditions dans votre code, qui alourdisse la lisibilité, vous pouvez préparer les noms de classe à l’aide de ce package.

7 – La déstructuration

ES6 a introduit le concept de déstructuration. La déstructuration vous permet de « séparer » les propriétés d’un objet ou des éléments d’un tableau.

Cela permet par exemple d’éviter la répétition très courante de this.props ou this.state avec React

La déstructuration d’un tableau est un moyen beaucoup plus simple d’accéder à un élément du tableau et permet d’éviter d’accéder à un élément du tableau par sa position ou sa clé.

8- Séparer la logique du rendu visuel

Ceci est un principe très important dans React, il existe plusieurs noms pour parler de ce pattern :

– Stateful vs Stateless

– Conteneur vs Composant de présentation

– Composants Smart vs Dumb

La différence est que certains composants possèdent un ‘état’ (state) et les autres sont sans ‘état’. Les composants stateful suivent l’évolution des données et peuvent effectuer des traitements en fonctions de ces données (Smart), tandis que les composants stateless affichent simplement ce qui leur est donné en paramètre, ou restituent toujours la même chose (Dumb).

Le fait de mélanger votre logique de chargement de données avec votre logique de rendu (ou de présentation) va augmenter la taille et la complexité de vos composants.

Dans l’exemple ci-dessous, les données utilisateurs sont chargées puis affichées dans un seul composant.

La bonne pratique est d’écrire un composant parent (Stateful) dont la seule responsabilité est la gestion des données qui appellera ensuite des composants enfants (Stateless) qui afficheront ces données ; ici RenderUser et Loading.

De plus cette séparation permet la mise en place de la règle vue précédemment dans la section « Diviser pour mieux régner ». Permettant ainsi l’utilisation de composant plus petit avec moins de responsabilités ; qui seront alors plus simple à lire, à tester, à maintenir et à réutiliser. 

9 – L’utilisation de conditions dans le JSX

false, null, undefined et true sont des enfants valides pour React. Cependant ils ne s’affichent simplement pas. Ce qui permet de s’en servir de condition pour restituer différents composants directement dans la fonction render de React.

10 – Les conditions ternaires

Les conditions ternaires imbriquées ne sont généralement pas une bonne idée. Plus les conditions sont complexes plus la lecture est difficile. Il est déconseillé de sacrifier la lisibilité pour un simple gain de syntaxe et de place.

Fractionner vos composants autant que possible est toujours une bonne chose, cela permet la mise en place d’évolution ou correction simplement et rapidement.  

11 – Use propTypes and defaultProps

Les propTypes et defaultProps sont des propriétés statiques, déclarées aussi haut que possible dans le code du composant. Elles devraient être immédiatement visibles par les autres développeurs qui lisent le fichier, car elles servent de documentation pour votre code.

Tous vos composants doivent avoir des propTypes dès qu’il possède des props

propTypes permettent de déclarer les différents types des paramètres props fournit à votre composant. Si un des types n’est pas respecté, un avertissement dans la console de votre navigateur sera ajouté. C’est semblable un contrat d’api. Cela permet de prévenir d’erreur dans votre code et de spécifier les types attendus de vos variables.

Ici, on déclare que le type attendu de la propriété name est une chaine de caractère.

Toutes les propriétés doivent avoir un defaultProps défini.

Comme son nom le laisse deviner defaultProps permet de déclarer la valeur par défaut d’une props. Cela permet d’éviter d’initialiser à la main dans votre code les props de votre composant et d’éviter les conditions pour s’assurer que toutes les props sont bien définies. Ainsi à l’initialisation de votre composant, les defaultProps définis seront fusionné avec vos props, évitant que vos propriétés aient pour valeur UNDEFINED. 

Ici, on définit pour la propriété name la valeur par défaut ‘Stranger’.

Les petits secrets de React – la fonction setState()

Cet article s’adresse aux personnes ayant un minimum de connaissance en React particulièrement sur les notions d’état et de cycle de vie dans un composant React.

Voici un petit secret de setState() – c’est en fait un appel asynchrone. React gère les changements d’état de cette façon pour des raisons de performances. Selon la documentation officielle de React : « React peut regrouper plusieurs appels setState () en une seule mise à jour ».

Il est donc possible que le state du composant ne change pas immédiatement après l’appel de setState(). Ce qui signifie surtout que vous ne devriez pas vous fier à la valeur actuelle du state lorsque vous appelez setState(), car vous ne pouvez pas être sûr de la valeur de celui-ci au moment de son exécution !

Cependant cela ne veut pas dire qu’utiliser la méthode setState() de la façon ci-dessous ne fonctionne pas :

Dans cet exemple simple, nous ne rencontrerions probablement aucun problème si nous nous basions sur la valeur du state, mais vous pouvez imaginer qu’une application qui devient de plus en plus complexe avec de nombreux appels setState() concurrents forme une file d’appel qui ne s’exécutent pas nécessairement dans l’ordre que vous souhaitez ou imaginez. La possibilité que la valeur de this.state.showForm ne corresponde pas à vos attentes est dans ce cas est bien réelle. Ce qui peut alors introduire des bugs aléatoires, très dur à reproduire et à localiser.

La solution pour éviter cela, consiste à passer une fonction à setState() avec la valeur du state précédent en tant qu’argument. Ce qui vous assure que dans un contexte d’appel concurrent à setState() votre appel se basera toujours sur la dernière valeur mise à jour du state.


Erreur commune avec setState() :

Il est très commun de vouloir effectuer des traitements après la mise à jour du state comme ceci :

Cependant, comme expliqué précédemment, setState() est une opération asynchrone, ce qui peut alors entrainer des comportements aléatoires de votre application. En effet, il est possible que les traitements suivants l’appel à setState() s’exécute avant la mise à jour de la valeur du state ou pire que celle-ci change pendant vos traitements, faussant alors absolument tout ! 

Si votre traitement doit absolument être réalisé après la mise à jour de la valeur du state, React à tout prévu ! La fonction setState() possède un callback, permettant l’exécution de votre code à la suite de la mise à jour du state.

Il est alors possible de rendre l’exécution de votre code synchrone comme ceci :

En conclusion :

La fonction setState() est puissante et essentielle dans React. Cependant son utilisation est délicate pour les débutants et même les programmeurs expérimentés peuvent introduire facilement des bugs très subtils avec cette fonction. Il faut alors être vigilant et appliquer les conseils de cet article même si cela peut sembler une perte de temps. Vous ne savez pas comment votre application va évoluer alors, autant anticiper au plus tôt des problèmes que peut introduire setState().

Pour éviter les difficultés liées à la méthode setState(), certains développeurs préfèreront utiliser MobX afin de gérer le state de leur application. MobX est une librairie censé simplifier la gestion du state en résolvant le problème fondamental: il est impossible de produire un state incohérent car MobX est synchrone. Cependant je n’ai pas encore eu l’occasion de tester cette solution par moi-même.