React

Elle peut ainsi être utilisée avec une autre bibliothèque ou un framework MVC comme AngularJS. La bibliothèque se démarque de ses concurrents par sa flexibilité et ses performances, en travaillant avec un DOM virtuel et en ne mettant à jour le rendu dans le navigateur qu'en cas de nécessité2.

Les chapitres de la formation React



                    

React est sans doute le framework front-end le plus populaire aujourd’hui, même s’il préfère se positionner comme une bibliothèque (library) plutôt que comme un framework. En effet, React se concentre sur le cœur du problème : la gestion de l’interface utilisateur. Les autres briques applicatives, comme le routage côté client, le stockage des données, etc. sont laissées aux innombrables solutions complémentaires de son écosystème (par exemple, React-Router, Redux, Redux-Offline…).

                    

Create-React-App est un outil pour faciliter le développement d’applications web fondées sur React. L’idée est de générer automatiquement un squelette applicatif et de masquer la complexité potentielle de configuration des briques techniques associées : gestion de JavaScript moderne (ES2015+), bundling de notre application (avec Webpack), serveur de développement, génération de fichiers de production optimisés, etc.

                    

L’univers React, comme ceux de la plupart des frameworks front-end, utilise désormais intensément des avancées récentes de JavaScript. Ces avancées sont apparues à partir de 2015, dans la version du langage appelée ES2015 (également connue sous le nom « ES6 »).

                    

React permet de définir des composants à l’aide d’une simple fonction. Ce type de composant, appelé « composant pur fonctionnel », doit idéalement toujours renvoyer la même chose pour les mêmes arguments, et ce sans effet de bord ni variation. On parle alors de fonctions pures. Cela le rend particulièrement facile à tester.

                    

Nous avons vu qu’il est possible de décrire nos DOM virtuels React (« grappes ») avec la fonction React.createElement(…) . Nous allons voir dans ce chapitre qu'il est préférable d'utiliser la syntaxe JSX, créée spécifiquement pour React.

                    

React facilite énormément la gestion des événements du DOM, notamment en fournissant une syntaxe pratique et concise tout en conservant des performances optimales.

                    

Jusqu’ici, le contenu de nos composants conservait la même structure d’une utilisation à l’autre. Mais naturellement, dans de véritables applications, certaines parties peuvent changer suivant le contexte. Comment retranscrire cela dans notre JSX ?

                    

Jusqu’ici nous n’avons utilisé que des éléments conditionnels ou en quantité fixe. Mais très fréquemment, nous aurons à représenter un ensemble dynamique de données (série, liste) sous forme de composants React. Par exemple des tâches, des utilisateurs, des cases d’un plateau de jeu…

                    

Une des clés de React est la notion que les données « descendent » à travers l’arborescence de composants. Le mécanisme-clé pour cela, que nous avons déjà vu en partie, ce sont les props.

                    

Il arrive hélas trop souvent qu’on oublie une prop, qu’on fasse une faute de frappe, ou qu’on y mette une valeur inappropriée. Le seul moyen pour un composant parent de « configurer » notre composant, de lui indiquer quoi faire, c’est de lui passer les bonnes props. Les props sont, véritablement, l’API de notre composant. À ce titre, il semble fondamental de définir formellement cette API, et donc la liste de nos props.

                    

La principale limite des fonctions, c’est qu’elles se restreignent en fait au rendu, c’est-à-dire à la constitution du DOM virtuel du composant, avec pour seule information entrante la liste des props issue du parent.

                    

Très fréquemment, nous aurons des méthodes métier qui seront passées par référence dans des props, notamment pour les gestionnaires d’événements et fonctions de rappel (callbacks). En JavaScript, le passage par référence d’une fonction « classique » (définie avec function , ou la syntaxe courte de méthode) ne définit pas le this associé

                    

Le problème ici est qu’à chaque render() produisant cette grappe, on crée de nouvelles fonctions pour les passer aux props : outre le coût de création et d’occupation mémoire associé, les composants ainsi paramétrés ont l’impression de recevoir des props différentes à chaque fois. Elles considéreront qu’ils devront re-renderer eux-mêmes, y compris lorsque cela est en fait superflu. Dans le second cas ci-dessus, la fonction fléchée à la volée est compréhensible, puisque l’appel de la méthode métier requiert une donnée fournie par la closure (ici le contexte en cours dans la fonction de rappel passée à map , pour aller y chercher item ). Or, dans le premier cas, la fonction fléchée n’est qu’un passe-plat : elle n’apporte aucun élément de contexte supplémentaire à la méthode métier invoquée. On voudrait pouvoir juste dire : <AwesomeConfirm onConfirm={this.triggerAwesome} /> Ainsi, on passe la même valeur au composant à chaque render() , ce qui est plus rapide, moins gourmand en mémoire, et peut éviter au composant des re-renderings superflus. Il y a un cependant un souci : en passant une référence à une fonction classique (déclarée avec function ou avec la syntaxe courte de méthode au sein d’un objet ou d’une classe), le this en vigueur disparaît : il ne fait pas « partie » de la référence passée.

                    

Nos composants ont pour le moment uniquement utilisé les props pour gouverner leur apparence et leur fonctionnement. Mais une fois nos composants renderés, ils commencent à « vivre » en interaction avec l’utilisateur ou le système. Il faut alors qu’ils puissent faire évoluer leurs données sous-jacentes, et pas forcément que les props bougent d'elles-mêmes.

                    

Ça envoie une série de modifications à l’état local du composant. Il faut bien comprendre que ce n’est pas le nouvel état dans son intégralité, mais juste des différences. C’est pratique, car ça évite de toujours devoir envoyer un état complet ; si on veut juste changer une propriété de l’état, on se contente de cette propriété : this.setState({ open: true }) // modifie uniquement cette propriété Les soucis commencent lorsqu’on oublie cet aspect « différentiel » et que l'on omet donc de réinitialiser certains champs.

                    

Les composants basés sur des classes passent par toute une série d’étapes au cours de leur vie. React nous permet de réagir à ces étapes en implémentant dans nos classes des méthodes aux noms spécifiques, appelées méthodes de cycle de vie.

                    

Pour vous entraîner, réalisez cet exercice étape par étape. Une fois terminé, vous pouvez comparer votre travail avec les pistes que je vous propose.

                    

Lorsque l’on travaille avec des champs de formulaires, on repose en pratique sur les définitions HTML et DOM de ces champs ; et c’est bien là le problème, car cet aspect des choses a évolué de façon si désorganisée au fil des ans, qu’on se trouve confronté aujourd’hui à un sacré sac de nœuds lorsque l’on manipule des champs de formulaires « en direct ». Heureusement, React ramène un peu de rationalité à tout cela.

                    

React propose deux façons de manipuler des champs de formulaires : l’approche contrôlée, utilisée la plus souvent, et l’approche non-contrôlée, moins fréquente. Dans ce chapitre, nous verrons en détail la version contrôlée. Le chapitre suivant présentera les champs non-contrôlés.

                    

Les champs non contrôlés sont pratiques lorsque l’on souhaite récupérer une valeur de champ sans contraindre sa saisie ni son formatage. Cela peut notamment se produire parce que ces champs sont déjà « contrôlés » par un mécanisme externe à React (utilisation d'une bibliothèque tierce partie comme un plugin jQuery, validation par HTML5 et DOM pur, etc.).

                    

La documentation officielle de React revient en détail sur les points abordés dans cette partie : Forms détaille la gestion des formulaires, notamment pour le mode contrôlé et la simplification des événements et valeurs. Uncontrolled Components précise la partie non contrôlée. Il existe par ailleurs de nombreux éléments dans l’écosystème pour faciliter la gestion des formulaires et autres champs personnalisés. Le fameux référentiel Awesome React a naturellement une section entière dédiée aux formulaires, et même une sous-partie spécifique aux saisies auto-complétées. De son côté, Awesome React Components a une liste complète de composants personnalisés dédiés à la saisie en formulaires.

                    

Pourquoi écrire des tests automatisés ? Tout simplement parce qu’on n’a pas de super-pouvoirs capables de geler le cours du temps dès qu’on touche une ligne de code, histoire de retester manuellement 100% des cas prévus (ce qui serait très faillible de toutes façons). Les tests automatisés nous permettent de vérifier, rapidement et de façon raisonnablement fiable, que nous ne « cassons rien » lorsque nous touchons au code, mais aussi évidemment que notre nouveau code fonctionne correctement. Ils nous permettent de déployer plus souvent—et plus sereinement—notre code en production.

                    

Jest est intégré de base avec les applications générées par Create React App. Si on regarde le fichier package.json à la racine du projet, on voit qu’il propose un script test dont la commande est react-scripts test --env=jsdom , lequel utilise Jest en interne. On trouvait également un fichier src/App.test.js qui contenait une suite de test basique, ce qu’on appelle un smoke test, qui vérifie simplement que notre composant <App/> arrive à réaliser son rendu sans problème.

                    

Jest fournit une série d’assertions déjà relativement étendue, et qui peut être augmentée par des plugins tiers. Toutefois, j’ai une faiblesse pour la syntaxe éminement lisible, et surtout chaînable, des assertions de Chai, qui sont indépendantes du harnais utilisé et sont extrêmement populaires.

                    

Si le renderer de test de React permet en effet de tester divers aspects de nos composants, il est généralement admis qu’il est très inférieur à l’outil Enzyme de Airbnb, lequel est d’ailleurs officiellement recommandé par Create React App. Enzyme nous permet de facilement tester nos composants en isolation, c’est-à-dire sans exécuter les render() des composants fils, ce qui est extrêmement pratique… et beaucoup plus rapide !

                    

Bien souvent, lorsque nous testons un composant, nous validons son interactivité en simulant des événements pour vérifier le bon déclenchement des comportements attendus. Notre composant <Card /> propose une prop onClick qu’il est effectivement censé appeler, avec sa position dans le tableau (sa prop index ) en argument, lorsqu’on clique sur son contenu. Comment vérifier ça ?

                    

Jest propose un mécanisme original : les snapshots. L’idée est de prendre une « photo » intégrale du résultat d’un morceau de code, en partant du principe que ce code marche correctement à ce moment-là, pour ensuite, lors des prochaines passes de test, reprendre une photo et comparer à celle de référence.

                    

Il est assez ardu de mesurer correctement la couverture de test d’un code transpilé par Babel ; le module de référence, Istanbul, nécessite pas mal de réglages pour pouvoir interpréter correctement les positions de code, caractère par caractère, en dépit de la transpilation éventuelle de syntaxes modernes telles que ES2017 ou JSX.

                    

Jest, pour les options, la configuration, le détail des assertions prédéfinies, et les aspects avancés comme le module mocking. Chai, pour le détail des assertions prédéfinies, chaînes de langage et plugins disponibles. Enzyme, pour le détail de l’API du Shallow Renderer, le fonctionnement du Mount Renderer si vous avez besoin du DOM, etc. Chai-Enzyme, pour les assertions correspondantes fournies par le plugin Sinon, pour le détail des système de spies, stubs et mocks, la simulation du temps et des accès réseau, et le super système de sandboxes. Sinon-Chai, pour les assertions correspondantes fournies par le plugin.