Plongée dans l'écosystème ReactJS

2022-02-14 | Benoît Petitcollot

image article

Plongée dans l'écosystème ReactJS

Je vais vous présenter dans cet article mon retour d'expérience sur le passage au développement front dans un écosystème centré sur la librairie javascript la plus répandue à ce jour : ReactJS.

Premier projet

J'ai expliqué dans un précédent article les enjeux du développement web en mode API-first. C'est dans ce contexte que j'ai développé pour la première fois un front entièrement en ReactJS.

Auparavant, j'avais l'habitude d'écrire des interfaces à l'aide du trio HTML-CSS-Javascript sans utiliser de bibliothèque particulière. En bon développeur "fullstack" mais quand même plutôt spécialiste de PHP, je n'ai jamais utilisé de préprocesseur CSS (SASS, LESS...). J'utilisais occasionnellement quelques bibliothèques javascript pour des besoins spécifiques (select2, chartJS...).

Côté frameworks, j'avais eu deux jours de formation sur Angular, et j'avais testé ReactJS et Ember mais je n'avais jamais utilisé aucun des trois pour un vrai projet. Aucune expérience non plus sur webpack. Comme vous le voyez, je partais d'assez loin...

Montée en compétence sur ReactJS

Or, un beau matin que je développais tranquille mon API que personne ne consommait, on m'a dit : "Ecoute, Loïc est très occupé sur un autre projet, il va falloir que tu écrives aussi le front." Je savais qu'il était prévu une architecture assez complexe à base de ReactJS, le framework NextJS mais avec un serveur custom, du rendu côté serveur (SSR), le tout permettant de réaliser une Progressive Web App (PWA), bref un truc très loin de mes petites habitudes.

Pour couronner le tout, j'ai pu jeter aux oubliettes une partie de mes souvenirs sur les classes ReactJS puisqu'on profitait de ce projet pour passer aux composants fonctionnels.

Notre intégrateur avait codé la structure HTML et le CSS avec la bibliothèque ReactJS styled-components. Loïc a écrit quelques Composants d'ordre supérieur (HOC), une base de serveur NextJS custom, puis il m'a laissé la main pour écrire l'essentiel des pages de l'application.

J'ai donc pu laisser de côté le CSS qui était embarqué dans des composants prêts à l'emploi. Je n'avais plus qu'à me concentrer sur la maîtrise de ReactJS et de ses hooks ainsi que sur NextJS et son rendu côté serveur.

Grâce à la revue de code systématique que nous pratiquons, j'ai pu avoir un retour constant sur mon code ReactJS, ce qui m'a permis d'être assez rapidement productif et de pouvoir m'intéresser ensuite aux aspects du projets qui m'avaient été épargnés au départ.

Nouvelles habitudes

L'utilisation de ReactJS nécessite la mise en œuvre des techniques de développement qui l'accompagnent. Voici quelques uns des principaux changements que cela a représenté dans mes pratiques.

ReactJS utilise la programmation déclarative

A l'origine, javascript est apparu dans les navigateurs pour manipuler la page web (le DOM) afin de la rendre interactive. On peut ainsi par exemple observer une série d'onglets et réagir quand l'utilisateur clique dessus pour faire apparaître le contenu correspondant. Tout cela se fait via une programmation impérative : on écrit un code qui réalise des actions concrètes sur le DOM. Ce type de programmation pose problème lorsque le code de l'application devient complexe car chaque bloc de code présuppose que le DOM est dans un état plus ou moins déterminé avant de réaliser l'action. Quand on implémente beaucoup d'actions, il est de plus en plus difficile d'être sûr qu'on ne perturbe pas le comportement des actions existantes.

Petit exemple de programmation impérative en javascript : un bouton qui permet de miner des bitcoins !

<p><span id="count">0</span> bitcoins</p>

<button id="mining">Miner !</button>

let count = 0;
// lorsqu'on clique sur le bouton ayant l'id "mining", ...
document.getElementById('mining').addEventListener('click', () => {
  // ... on modifie la valeur du compte de bitcoins directement dans le DOM.
  document.getElementById('count').innerText = ++count;
});
// Si un autre bout de code modifie la valeur affichée, elle risque de ne plus
// être égale à la valeur de la variable...

Pour résoudre cette difficulté, ReactJS adopte un style de programmation déclarative : on écrit les différents affichages possibles de la page en utilisant des variables qui indiquent si tel ou tel élément doit être affiché ou masqué. On peut ensuite écrire des actions qui modifient la valeur de ces variables et c'est ReactJS qui se charge de reconstruire le nouvel affichage avec ces nouvelles valeurs. Ce type de programmation génère beaucoup moins d'effets de bords. Personnellement, je m'attendais à ce que cette technique grève les performances mais il n'en est rien. ReactJS ne reconstruit que le nécessaire à chaque changement d'état et optimise l'accès au DOM.

Pour mettre en œuvre cette technique, ReactJS propose d'utiliser JSX qui permet d'écrire un code dont la syntaxe se rapproche fortement de HTML. C'est un langage composé de balises imbriquées avec des attributs précisant leur comportement. Chaque balise est transformée en bloc de code javascript lors de la compilation. Cela permet de travailler en ayant sous les yeux un code qui se rapproche de l'apparence finale du HTML qu'on construit, rendant ainsi le développement plus intuitif.

Réécrivons notre ferme à bitcoin en ReactJS :

// ReactJS met à disposition un mécanisme de gestion d'état permettant d'obtenir
// une variable ("count") et une fonction de modification ("setCount").
const [count, setCount] = useState(0);

// Ces balises JSX sont en fait des fonctions javascript déguisées en html.
return <>
    <P>{count} bitcoins</P>

    <Button
      onClick={() => {
        // Quand on clique sur le bouton, on ne modifie que la valeur de la variable.
        // On ne touche pas au DOM, c'est ReactJS qui en est chargé.
        setCount(count + 1);
      }}
    >
      Miner !
    </Button>
  </>
  // Si un autre bout de code veut modifier la valeur affichée, il ne pourra
  // le faire qu'en modifiant la valeur de la variable avec la fonction setCount.
  // ReactJS garantit ainsi que l'affichage est toujours synchronisé avec la variable.

ReactJS organise le code en Composants

Les blocs de code représentés par les balises JSX sont en réalité des composants. Plutôt que de permettre à n'importe quel code javascript de manipuler n'importe quoi dans le DOM, l'architecture par composants réalise le contrat suivant : le code d'un composant ne manipule que le composant lui-même. Si le composant doit entraîner des modifications à l'extérieur, il se contente de transmettre l'information à son composant parent (celui qui le contient) et c'est le parent qui réalisera les modifications nécessaires.

Il faut s'habituer à cette restriction mais cela permet une meilleure organisation du code. Par ailleurs, chaque composant devient un bloc autonome qui peut plus facilement être réutilisé à différents emplacements. C'est d'ailleurs ce qui a permis la répartition du travail sur le projet : l'intégrateur a réalisé des composants ReactJS chargés d'appliquer les règles CSS et de transmettre les actions (clic, survol, etc.) au parent. Mon rôle était ensuite d'imbriquer ces composants sans avoir à me soucier de leur apparence. Je n'avais plus qu'à implémenter la logique liée aux actions de l'utilisateur.

HTML et CSS générés par du javascript

Le plus gros changement pour moi a sans doute été que l'ensemble du site est écrit en javascript. Même si JSX donne l'impression d'écrire quelque chose qui ressemble à HTML, même si styled-components ressemble à des templates CSS, au final tout celà est compilé en javascript et c'est l'exécution de ce javascript qui fournit le HTML/CSS final. Cela rend le site beaucoup plus sensible aux bugs : à la moindre erreur d'exécution, la page ne peut plus s'afficher et on obtient une page d'erreur. Ce problème a justifié l'introduction, depuis la version 16 de ReactJS, des périmètres d'erreur, un type de composants chargés d'intercepter les erreurs risquant de casser l'ensemble de l'application. Lorsque javascript n'est utilisé que comme une couche de programmation ajoutée sur une page HTML, une erreur dans le code n'entraîne qu'une perte d'interactivité de la page mais le contenu HTML s'affiche tout de même.

Il faut cependant voir cela comme un avantage. Les erreurs sont plus faciles à détecter, surtout avec des outils de notification automatique d'erreur comme Sentry, et leur résolution pourra être plus rapidement prise en charge. Par ailleurs, chaque composant étant fonctionnellement autonome, il est aussi plus facile à tester unitairement.

Conclusion

Passer de HTML/CSS/Javascript à ReactJS/NextJS, c'est un peu comme passer d'une 2CV à la dernière voiture à la mode avec toutes les options. Heureusement que j'avais un copilote pour me montrer sur quels boutons il fallait appuyer. Par la suite, ce sont des habitudes très différentes et de gros gains de productivité. On s'agace parfois quand quelque chose ne marche pas et on est obligé de lire un mode d'emploi épais comme une encyclopédie. Mais on ne remonte pas dans sa 2CV pour autant. Ou alors juste pour de petits trajets et avec un brin de nostalgie...

ReactJS JavaScript HTML CSS