React.js View State Management Tutoriel

Publié: 2022-03-11

L'un des problèmes les plus importants et les plus courants dans le développement Web frontal est la gestion de l'état. Les développeurs front-end indépendants comme moi sont constamment concentrés sur la synchronisation de l'objet d'état avec sa vue et la représentation DOM. Les utilisateurs peuvent interagir avec l'application de plusieurs manières et c'est une tâche importante que de fournir une transition propre d'un état à un autre.

Avec ce tutoriel React.js, apprenez-en plus sur la gestion de l'état d'affichage.

Un peu d'histoire

Il n'y a pas si longtemps, les applications Web avaient un flux de données beaucoup plus simple. Le navigateur enverrait une requête au serveur, toute la logique d'application serait exécutée sur le serveur et une vue HTML complète serait renvoyée au navigateur pour être présentée à l'utilisateur. Les actions ultérieures de l'utilisateur (telles que les clics, les soumissions de formulaires, etc.) déclencheraient à nouveau le même flux. Les applications n'avaient pas à se soucier de l'état de l'utilisateur et chaque vue pouvait être régénérée en envoyant une nouvelle requête au serveur.

Cependant, les applications Web ont gagné en complexité et les demandes des utilisateurs vis-à-vis de l'interface utilisateur/UX ont également progressé. Le rechargement de la page entière, lorsqu'une seule partie de celle-ci change, était inefficace et lent. Nous avions besoin d'une interaction rapide, réactive et réactive avec un impact immédiat sur l'interface utilisateur.

JavaScript est venu à la rescousse. Les développeurs ont commencé à écrire des quantités importantes de code qui ont été exécutés dans le navigateur avant qu'une requête ne soit envoyée au serveur. jQuery a également apporté des avancées significatives au développement Web frontal, car il fournissait des fonctionnalités prêtes à l'emploi simples et efficaces telles que la validation côté client, les fenêtres modales, les messages d'alerte, les animations et même les mises à jour partielles de pages basées sur Ajax.

Comprendre la complexité

Examinons un exemple simple d'évaluation de la force d'un mot de passe. Si le mot de passe est OK, la zone de saisie doit avoir une bordure verte et doit afficher un joli message. Si le mot de passe est faible, la zone de saisie doit avoir une bordure rouge et doit afficher un message d'avertissement. Nous pourrions également afficher un visage souriant lorsqu'un mot de passe est suffisamment fort.

Le code suivant montre comment cela pourrait être fait par manipulation DOM. Il y a beaucoup de "si" ici, et le code n'est pas très facile à lire.

 if (hasInputBorder()) { removeInputBorder(); } if (text.length === 0) { if (hasMessage()) { removeMessage(); } if (hasSmiley()) { removeSmiley(); } } else { var strength = getPasswordStrength(text); if (!hasInputBorder()) { addInputBorder(); } var color = (strength == 'weak' ? 'red' : 'green'); setInputBorderColor(color); var message = (strength == 'weak' ? "Password is weak" : "That's what I call a password!"); if (hasMessage()) { setMessageText(message); } else { addMessageWithText(message); } if (strength == 'weak') { if (hasSmiley()) { removeSmiley(); } } else { if (!hasSmiley()) { addSmiley(); } } }

Comme indiqué ci-dessus, nous devons d'abord vérifier si l'utilisateur a fourni un mot de passe et gérer le cas où le champ du mot de passe est vide. Et dans tous les cas, nous devons nous assurer que tous les éléments DOM associés sont correctement mis à jour. Cela inclut le message, la bordure et le visage souriant.

Notre champ de mot de passe peut être dans l'un des trois états suivants : vide, faible ou fort. Et comme indiqué, nous avons trois éléments DOM différents qui sont affectés par l'état du champ de mot de passe. Gérer toutes les combinaisons et s'assurer que notre vue est correctement affichée augmente la complexité cyclomatique, même pour un simple morceau de code comme celui-ci.

Le DOM fonctionne en mode retenu , ce qui signifie qu'il ne se souvient que de l'état actuel. Afin de modifier notre vue, nous devons fournir des instructions pour chaque élément DOM et programmer la transition.

Le codage des transitions au lieu des états peut être complexe. Le nombre de branches et de vérifications que nous devons effectuer dans notre code augmente de façon exponentielle avec le nombre d'états d'affichage à gérer.

Dans notre exemple, nous avons défini trois états de vue, ce qui nous a donné 3 * 2 = 6 transitions. En général, étant donné N états, nous avons N * (N - 1) = N^2 - N transitions que nous aurions besoin de modéliser. Pensez simplement à la complexité accrue si nous ajoutions un quatrième état à notre exemple.

Il y a généralement trop de code lié à la modélisation des transitions . Ce serait bien mieux si nous pouvions simplement définir nos états de vue et ne pas nous soucier de tous les détails de la transition d'un état à un autre.

Réduire la complexité

En supposant que nous puissions déclarer l'état de la vue en fonction de l'état du modèle, au lieu de coder explicitement la transition d'un état à un autre, nous pourrions avoir quelque chose comme ceci :

 var strength = getPasswordStrength(text); if (text.length == 0) { return div(input({type: 'password', value: text})); } else if (strength == 'weak') { return div( input({type: 'password', value: text, borderColor: 'red'}), span({}, "Weak") ); } else { return div( input({type: 'password', value: text, borderColor: 'green'}), span({}, "That's what I call a password!"), img({class: 'icon-smiley'}) ); }

Ici, nous avons trois branches de code simples, représentant les trois états possibles de notre application. Nous renvoyons simplement la spécification de la vue dans chaque branche, en fonction de l'état du modèle. Tout le code de manipulation DOM est supprimé ; nous fournissons simplement les informations sur ce que nous voulons, et non sur la façon d'y arriver.

Bien que cette approche réduise considérablement la complexité du code, elle suppose également qu'il y a quelqu'un ou quelque chose d'autre pour s'occuper de la manipulation réelle du DOM en notre nom.

C'est là que React entre en jeu. React s'assurera qu'un état d'affichage est immédiatement géré et mis à jour en fonction de l'état du modèle de données sous-jacent.

Réaction

React est une bibliothèque JavaScript créée par Facebook. Il est conçu pour gérer la partie interface utilisateur des applications Web. Vous pouvez le considérer comme le V dans l'architecture MVC. Il est très concentré. Il ne fait aucune hypothèse sur le reste de votre pile technologique et il ne gère rien d'autre que le rendu des composants. Il ne fournit pas de mécanismes de routage, de modèles ou d'autres fonctionnalités qui sont généralement regroupées dans des frameworks plus larges. Ainsi, vous pouvez le mélanger et l'utiliser avec n'importe quelle autre bibliothèque ou framework de votre choix.

React nous permet de définir les UI comme des arborescences de composants composites. Un développeur React définit ces composants en spécifiant une fonction de rendu qui décrit le composant, compte tenu de l'état d'entrée. Cette fonction doit être pure (c'est-à-dire qu'elle ne doit pas avoir d'effet secondaire ni dépendre d'autre chose que de son entrée explicite).

La fonction de rendu renvoie une description de la vue, que React appelle un DOM virtuel . Considérez-le comme un objet JavaScript correspondant à l'élément DOM rendu.

Lorsque vous modifiez l'état du composant, il se restitue simplement lui-même et tous ses éléments enfants, renvoyant un nouveau DOM virtuel .

De plus, React ne fera pas un simple remplacement HTML, lors de la transition d'un état à un autre. Il trouvera la différence entre l'état précédent et le nouvel état et calculera l'ensemble d'opérations DOM le plus efficace pour exécuter une transition.

Même sans tenir compte des performances, la réduction de la complexité du code est elle-même significative et nous permet de concentrer nos efforts sur les parties les plus uniques et les plus complexes de notre application.

Pour être un peu plus concret, voici comment notre exemple de tutoriel serait réalisé en utilisant React pour gérer les états de vue.

REMARQUE : L'exemple de code suivant est écrit dans le préprocesseur JSX, qui est un moyen courant d'écrire une interface utilisateur basée sur React.

 function getPasswordStrength(text) { // Some code that calculates the strength given the password text. } var PasswordWithStrength = React.createClass({ getInitialState: function() { return {value: ''}; }, render: function() { var strength = getPasswordStrength(this.state.value); if (this.state.value.length == 0) { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} /> </div>; } else if (strength == 'weak') { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid red'} } /> <span style={{color: 'red'}}>Weak!</span> </div>; } else { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid green'} } /> <span style={{color: 'green'}}>That's what I call a password!</span> <Emoji value="smiley" /> </div>; } }, handleInputChange: function(ev) { this.setState({value: ev.target.value}); } }); React.render(<PasswordWithStrength />, document.body);

Le composant Emoji qui est rendu lorsque la force du mot de passe est OK avec <Emoji value="smiley" /> est juste un autre composant personnalisé (tout comme PasswordWithStrength ). Il est défini comme ceci :

 var Emoji = React.createClass({ render: function() { var emojiSrc = this.props.value + '.png'; return <img src={emojiSrc}></img>; } });

React.js vs autres

En toute honnêteté, cependant, il existe d'autres frameworks JavaScript côté client (tels que Ember, Angular, Knockout et autres) qui ont également résolu le problème de gestion de l'état d'affichage, et y ont même ajouté plus de fonctionnalités. Alors, pourquoi voudriez-vous utiliser React au lieu de tout autre framework ?

React présente deux avantages clés par rapport à la plupart des autres bibliothèques.

Aucune liaison de données

Certains des autres frameworks alternatifs utilisent la liaison de données pour mapper les éléments DOM aux propriétés d'état et les maintenir synchronisés en observant les modifications de propriété. Cette approche permet de rendre la vue une seule fois, chaque changement ne déclenchant alors que les modifications des éléments DOM concernés. D'autres alternatives utilisent la vérification sale ; c'est-à-dire qu'au lieu d'observer les changements de propriété individuels, ils effectuent simplement un diff entre l'état précédent et le nouveau. React est plus similaire à cette dernière approche mais, au lieu de comparer les états, il compare les représentations de vue.

React n'a pas de liaison de données. Un développeur est censé appeler la méthode setState ou restituer le composant supérieur lorsque l'état est modifié. Il embrasse un flux unidirectionnel, de l'état à la vue.

Ce concept est facile à adopter car les développeurs ne pensent généralement pas à la liaison de données. L'accent est mis sur la représentation visuelle des données. Vous n'avez donc pas besoin de penser aux propriétés dépendantes, au formatage, à la liaison de balises HTML spéciales, etc. Avec React, vous re-rendez simplement le composant lorsque le modèle change.

Pour comprendre la différence de gestion de l'état de vue ici, comparons Ember et React . Nous allons créer un objet person qui affichera le nom complet en majuscules. Après deux secondes, nous allons simuler le changement et mettre à jour la vue.

 // EXAMPLE USING EMBER App = Ember.Application.create(); App.Person = Ember.Object.extend({ firstName: null, lastName: null, fullName: function() { return this.get('firstName') + ' ' + this.get('lastName'); }.property('firstName', 'lastName') }); var person = App.Person.create({ firstName: "John", lastName: "Doe" }); Ember.Handlebars.helper('upcase', function(value) { return value.toUpperCase(); }); App.IndexRoute = Ember.Route.extend({ model: function () { return person; } }); setTimeout(function() { person.set('firstName', 'Harry'); }, 2000); // Templates: <script type="text/x-handlebars"> <h2>Welcome to Ember.js</h2> {{outlet}} </script> <script type="text/x-handlebars" data-template-name="index"> The current user is: {{upcase model.fullName}} </script>

Nous avons créé un objet avec les propriétés firstName , lastName et fullName . Étant donné qu'Ember observe les changements de propriété, nous avons dû spécifier que fullName dépend du firstName et du lastName . Pour ce faire, nous avons ajouté .property('firstName', 'lastName') lorsque nous avons défini le fullName .

Après deux secondes, person.set('firstName', 'Harry'); est exécuté. Cela a déclenché la mise à jour de la vue et de sa liaison.

Faisons maintenant la même chose dans React.

 // EXAMPLE USING REACT var CurrentUser = React.createClass({ render: function() { return <div>The current user is: {this.props.user.fullName().toUpperCase()}</div>; } }); var person = { firstName: 'John', lastName: 'Doe', fullName: function() { return this.firstName + ' ' + this.lastName; } }; var currentUser = React.render(<CurrentUser user={person}/>, document.body); setTimeout(function() { person.firstName = 'Harry'; currentUser.setProps({user: person}); }, 2000);

Même si le code Ember est simple et facile à lire, il est évident que React gagne en simplicité. La person est un objet JavaScript simple, le fullName étant simplement une fonction.

Pas de modèle

Chaque framework alternatif a une manière différente de gérer les modèles. Certains d'entre eux utilisent des chaînes qui sont compilées en JavaScript, tandis que d'autres utilisent directement des éléments DOM. La plupart d'entre eux utilisent des attributs et des balises HTML personnalisés qui sont ensuite "compilés" en HTML.

Les modèles ne font pas partie du code JavaScript. Pour cette raison, chaque alternative a besoin d'une manière personnalisée de représenter les opérations courantes, les conditions, les itérations, les fonctions d'appel, etc. Elles finissent toutes par créer un nouveau pseudo-langage que les développeurs doivent apprendre.

Il n'y a pas de modèles dans React, tout est simplement du vieux JavaScript.

React utilise toute la puissance de JavaScript pour générer la vue. La méthode de rendu du composant est une fonction JavaScript.

JSX est disponible en tant que préprocesseur qui transforme la "syntaxe de type HTML" en JavaScript normal, mais JSX est facultatif et vous êtes libre d'utiliser du JavaScript standard sans aucun préprocesseur. Vous pouvez également tirer parti des outils JavaScript existants. Linters, préprocesseurs, annotations de type, minification, élimination du code mort, etc.

Prenons à nouveau un exemple concret pour comparer React à l'un des frameworks alternatifs de gestion des états de vue.

Le didacticiel suivant est un exemple d'utilisation d' AngularJS pour répertorier les hashtags et le nombre de tweets pour chacun d'eux. La liste est triée par nombre et un message s'affiche s'il n'y a pas de hashtags.

 <!-- EXAMPLE USING ANGULAR --> <div ng-controller="MyCtrl"> <ul ng-show="hashTags.length > 0"> <li ng-repeat="hashTag in hashTags | orderBy:'tweetCount':true"> {{hashTag.name}} - {{hashTag.tweetCount}} tweets </li> </ul> <span ng-show="hashTags.length == 0">No hashtags found!</span> </div>

Pour pouvoir faire cette liste, un développeur doit se renseigner sur les AngularJS directives , ng-show et ng-repeat . Ensuite, il doit en savoir plus sur les AngularJS filters pour comprendre orderBy . Beaucoup de travail pour une chose simple comme produire une liste !

Considérons maintenant l'exemple React qui fait la même chose :

 // EXAMPLE USING REACT function byTweetCountDesc(h1, h2) { return h2.tweetCount - h1.tweetCount; } //... render: function() { if (this.state.hashTags.length > 0) { var comps = this.state.hashTags.sort(byTweetCountDesc).map(function(hashTag, index) { return <li key={index}> {hashTag.name} - {hashTag.tweetCount} tweets </li>; }); return <ul>{comps}</ul>; } else { return <span>No hashtags found!</span> } }

Même si nous avons utilisé l'approche "plus avancée" et JSX, chaque développeur Web ayant une compréhension de base de JavaScript peut facilement lire le code ci-dessus et comprendre ce qu'il fait. La vérification conditionnelle standard utilisant if , l'itération utilisant map() , et un 'sort()' standard vient naturellement à tout développeur, il n'y a donc pas de syntaxe supplémentaire ou d'autres concepts à apprendre.

Conclusion

Le principal avantage de ce didacticiel React.js est le fait que React vous permet de vous concentrer sur la gestion réelle de l'état d'affichage plutôt que sur les transitions, simplifiant ainsi votre travail et votre application.

La courbe d'apprentissage pour adopter React est assez triviale. Aucun langage de template personnalisé à maîtriser, aucune liaison de données à craindre, et tout se résume aux fonctions JavaScript qui décrivent les éléments de l'interface utilisateur.

Pour en savoir plus sur la simplification du code de votre application à l'aide de React, jetez un œil à cette conférence de Steven Luscher, Decomplexifying Code with React.

Voici quelques lectures supplémentaires pour tous ceux qui souhaitent passer à l'étape suivante et commencer à utiliser React :

  • http://jlongster.com/Removing-User-Interface-Complexity,-or-Why-React-is-Awesome