Une introduction à React qui nous manquait

React est la bibliothèque JavaScript la plus populaire au monde. Mais cette bibliothèque n'est pas bonne parce qu'elle est populaire, mais parce qu'elle l'est parce qu'elle est bonne. La plupart des didacticiels d'introduction React existants commencent par des exemples d'utilisation de la bibliothèque. Mais ces guides ne disent rien sur les raisons pour lesquelles React est le bon choix.



Cette approche a ses atouts. Si quelqu'un s'efforce de s'entraîner tout de suite tout en maîtrisant React, il lui suffit de consulter la documentation officielle et de se mettre au travail . Ce matériel ( ici , si ça vous intéresse, sa version vidéo) est écrit pour ceux qui veulent trouver une réponse aux questions suivantes: «Pourquoi réagir? Pourquoi React fonctionne-t-il de cette façon? Pourquoi les API React sont-elles conçues telles quelles? "











Pourquoi réagir?



La vie devient plus facile si les composants ne sont pas conscients de la communication réseau, de la logique métier de l'application ou de son état. De tels composants, recevant les mêmes paramètres d'entrée, forment toujours les mêmes éléments visuels.



Lorsque la bibliothèque React est arrivée, elle a fondamentalement changé le fonctionnement des frameworks et bibliothèques JavaScript. Alors que d'autres projets similaires ont promu les idées de MVC, MVVM et autres, React a adopté une approche différente. A savoir, ici le rendu du composant visuel de l'application a été isolé de la présentation du modèle. Grâce à React, une toute nouvelle architecture est apparue dans l'écosystème du frontend JavaScript - Flux.



Pourquoi l'équipe React a-t-elle fait cela? Pourquoi cette approche est-elle meilleure que celles qui l'ont précédée, comme l'architecture MVC et le code spaghetti écrit en jQuery? Si ces questions vous intéressent, vous pouvez regarder cette conférence de 2013 sur le développement d'applications JavaScript sur Facebook.



En 2013, Facebook vient de terminer un travail d'intégration sérieux dans sa plateforme de chat. Cette nouvelle fonctionnalité a été intégrée à presque toutes les pages du projet, le chat a influencé les scénarios habituels de travail avec la plate-forme. C'était une application complexe intégrée dans une autre application qui n'avait pas été facile auparavant. L'équipe Facebook a dû faire face à des problèmes non triviaux, face à une mutation DOM incontrôlée et à la nécessité de fournir une expérience utilisateur asynchrone parallèle dans le nouvel environnement.



Par exemple, comment savoir à l'avance ce qui sera affiché à l'écran dans une situation où n'importe quoi, à tout moment et pour quelque raison que ce soit, peut accéder au DOM et y apporter des modifications? Comment s'assurer que ce que voit l'utilisateur est correctement tracé?



En utilisant des outils frontaux populaires qui existaient avant React, rien de tel ne pouvait être garanti. Dans les premières applications Web, la "condition de concurrence" dans le DOM était l'un des problèmes les plus courants.



Manque de déterminisme = calcul parallèle + état mutable.



Martin Oderski




La tâche principale de l'équipe de développement de React était de résoudre ce problème. Ils l'ont traité avec deux grandes approches innovantes:



  • Liaison de données unidirectionnelle utilisant l'architecture Flux.
  • Immuabilité de l'état des composants. Une fois l'état d'un composant défini, il ne peut plus être modifié. Les changements d'état n'affectent pas les composants rendus. Au lieu de cela, de tels changements conduisent à la sortie d'une nouvelle vue avec un nouvel état.


Le moyen le plus simple que nous ayons trouvé pour structurer et rendre les composants, d'un point de vue conceptuel, était de simplement viser zéro mutation.



Tom Ochchino, JSConfUS 2013




La bibliothèque React a pu réduire considérablement le problème des mutations incontrôlées en utilisant l'architecture Flux. Au lieu d'attacher des gestionnaires d'événements pour déclencher des mises à jour DOM sur un nombre arbitraire d'objets (modèles) arbitraires, la bibliothèque React a donné aux développeurs un moyen unique de gérer l'état des composants. Il s'agit de la répartition des actions qui affectent l'entrepôt de données. Lorsque l'état du magasin change, le système demande le rendu du composant.





L'architecture de Flux



Quand on me demande pourquoi je devrais prêter attention à React, je donne une réponse simple: "Le fait est que nous avons besoin d'un rendu déterministe des vues, et React rend cette tâche beaucoup plus facile."



Notez que lire des données depuis le DOM pour implémenter une logique est un anti-pattern. Quiconque fait cela va à l'encontre du but de l'utilisation de React. Au lieu de cela, les données doivent être lues à partir du magasin et des décisions basées sur ces données doivent être prises avant que les composants correspondants ne soient rendus.



Si le rendu déterministe des composants était la seule chose à propos de React, cela seul serait une grande innovation. Mais l'équipe de développement de React ne s'est pas arrêtée là. Cette équipe a présenté au monde une bibliothèque avec d'autres fonctionnalités intéressantes et uniques. Et au fur et à mesure que le projet évoluait, React a ajouté des éléments encore plus utiles.



JSX



JSX est une extension JavaScript qui vous permet de créer de manière déclarative des composants d'interface utilisateur. JSX présente les caractéristiques notables suivantes:





Si, avant l'avènement de JSX, il était nécessaire de décrire de manière déclarative les interfaces, alors il était impossible de se passer de modèles HTML. À l'époque, il n'existait pas de norme généralement acceptée pour créer de tels modèles. Chaque framework utilisait sa propre syntaxe. Cette syntaxe devait être apprise par quelqu'un qui, par exemple, avait besoin de parcourir certaines données, d'incorporer des valeurs de variables dans un modèle de texte ou de décider quel composant d'interface afficher et lequel non.



De nos jours, si vous regardez différents outils frontaux, vous constaterez que vous *ngForne pouvez pas vous passer d'une syntaxe spéciale, comme une directive d'Angular. Mais, comme JSX peut être appelé un sur-ensemble de JavaScript, la création de balisage JSX peut tirer parti des capacités JS existantes.



Par exemple, vous pouvez parcourir un ensemble d'éléments à l'aide de la méthode Array.prototype.map. Vous pouvez utiliser des opérateurs logiques, organiser le rendu conditionnel à l'aide de l'opérateur ternaire. Vous pouvez utiliser des fonctions pures , vous pouvez construire des chaînes à l'aide de littéraux de modèle . En général, toutes les fonctionnalités JavaScript sont disponibles pour ceux qui décrivent les interfaces dans JSX. Je pense que c'est un énorme avantage de React par rapport aux autres frameworks et bibliothèques.



Voici un exemple de code JSX:



const ItemList = ({ items }) => (
  <ul>
    {items.map((item) => (
      <li key={item.id}>
        <div>{item.name}</div>
      </li>
    ))}
  </ul>
);


Certes, lorsque vous travaillez avec JSX, vous devez prendre en compte certaines fonctionnalités qui, au début, peuvent sembler inhabituelles.



  • , , HTML. , class className. camelCase.
  • , , , JSX- key. . id, key.


React n'impose pas au développeur la seule manière correcte de travailler avec CSS. Par exemple, vous pouvez transmettre un objet JavaScript avec des styles à un composant en l'écrivant dans une propriété style. Avec cette approche, la plupart des noms de styles familiers seront remplacés par leurs équivalents camelCase. Mais les possibilités de travailler avec les styles ne se limitent pas à cela. En pratique, j'utilise simultanément différentes approches pour styliser les applications React. L'approche que vous choisissez dépend de votre style. Par exemple, j'utilise des styles globaux pour styliser les thèmes d'application et les mises en page, et les styles locaux pour personnaliser l'apparence d'un composant spécifique.



Voici mes caractéristiques de style React préférées:



  • CSS-, . , . — , .
  • CSS- — CSS- . JavaScript-. CSS-, . Next.js, , .
  • Le package styled-jsx , qui vous permet de déclarer des styles directement dans le code de votre composant React. Cela revient à utiliser une balise <style>en HTML. La portée de ces styles peut être appelée "hyperlocale". Le fait est que les styles n'affectent que les éléments auxquels ils sont appliqués et leurs enfants. Lorsque vous utilisez Next.js, le package styled-jsx peut être utilisé sans avoir besoin de vous connecter et de configurer quelque chose vous-même.


Événements synthétiques



React nous fournit un wrapper multi-navigateurs SyntheticEventsqui représente des événements synthétiques et est conçu pour unifier le travail avec les événements DOM. Les événements synthétiques sont utiles pour plusieurs raisons:



  1. , . .
  2. . , , , JavaScript HTML, . . , , React- .
  3. . . , , . , , , . . , . , JavaScript, .


Notez qu'en raison de l'utilisation du pool d'événements, les propriétés d'un événement synthétique ne sont pas accessibles à partir d'une fonction asynchrone. Pour implémenter un tel schéma de travail, vous devez prendre les données de l'objet événement et les écrire dans une variable accessible à la fonction asynchrone.



Cycle de vie des composants



Le concept de cycle de vie des composants React se concentre sur la protection de l'état du composant. L'état du composant ne doit pas changer pendant son affichage. Ceci est réalisé grâce au schéma de travail suivant: le composant est dans un certain état et rendu. Ensuite, grâce aux événements du cycle de vie, il devient possible de lui appliquer des effets, vous pouvez influencer son état, travailler avec des événements.



Comprendre le cycle de vie des composants React est extrêmement important pour développer des interfaces et en même temps ne pas se battre avec React, mais utiliser cette bibliothèque comme prévu par ses développeurs. Les «batailles» avec React, telles que la modification incorrecte de l'état des composants ou la lecture de données depuis le DOM, annulent les atouts de cette bibliothèque.



Dans React, à partir de la version 0.14, il existait une syntaxe de description de composant basée sur les classes qui vous permet de gérer les événements du cycle de vie des composants. Il existe trois étapes critiques dans le cycle de vie des composants React: le montage, la mise à jour et le démontage.





Cycle de vie des composants



L'étape de mise à jour peut être divisée en trois parties: Rendu (rendu), Precommit (préparation des modifications apportées à l'arborescence DOM), Validation (modification de l'arborescence DOM).





La structure de l'étape de mise à jour



Arrêtons-nous plus en détail sur ces étapes du cycle de vie des composants:



  • Render — . render() , . , JSX.
  • Precommit — DOM, getSnapShotBeforeUpdate. , , .
  • Commit - Au cours de cette phase du cycle de vie du composant, React met à jour le DOM et les refs . Ici, vous pouvez utiliser une méthode componentDidUpdateou un hook useEffect. C'est ici que vous pouvez effectuer des effets, planifier des mises à jour, utiliser le DOM et d'autres tâches similaires.


Dan Abramov a préparé un excellent diagramme qui illustre le fonctionnement des mécanismes du cycle de vie des composants.





Le cycle de vie des composants React À



mon avis, représenter les composants comme des classes à longue durée de vie n'est pas le meilleur modèle mental React. N'oubliez pas que l'état des composants React ne doit pas muter. L'état obsolète doit être remplacé par un nouveau. Chaque remplacement de ce type entraîne le rendu du composant. Cela donne à React ce qui est sans doute sa fonctionnalité la plus importante et la plus précieuse: la prise en charge d'une approche déterministe du rendu des visuels des composants.



Ce comportement est mieux pensé comme ceci: chaque fois que le composant est rendu, la bibliothèque appelle une fonction déterministe qui renvoie JSX. Cette fonction ne doit pas invoquer ses propres effets secondaires. Mais elle, si elle en a besoin, peut transmettre des demandes à React pour effectuer de tels effets.



En d'autres termes, il est logique de considérer la plupart des composants React comme des fonctions pures qui prennent des paramètres d'entrée et renvoient JSX. Les fonctions pures présentent les caractéristiques suivantes:



  • Lorsqu'ils ont la même entrée, ils renvoient toujours la même sortie (ils sont déterministes).
  • Ils n'ont aucun effet secondaire (c'est-à-dire qu'ils ne fonctionnent pas avec les ressources réseau, ne sortent rien sur la console, n'écrivent rien sur, localStorageetc.).


Notez que si des effets secondaires sont nécessaires pour qu'un composant fonctionne, vous pouvez les exécuter en utilisant useEffectou en faisant référence au créateur d'action transmis au composant via des accessoires et en permettant aux effets secondaires d' être traités en dehors du composant .



Crochets de réaction



React 16.8 introduit un nouveau concept appelé les hooks React. Ce sont des fonctions qui vous permettent de vous connecter aux événements du cycle de vie des composants sans utiliser la syntaxe de classe et sans dépendre des méthodes de cycle de vie des composants. En conséquence, il est devenu possible de créer des composants non pas sous forme de classes, mais sous forme de fonctions.



L'appel d'un hook, en général, signifie un effet secondaire - un effet qui permet au composant de fonctionner avec son état et avec le sous-système d'E / S. Un effet secondaire est tout changement d'état visible en dehors de la fonction, à l'exception d'un changement de la valeur renvoyée par la fonction. Utilisation du



crochetvous permet de mettre en file d'attente les effets secondaires pour une exécution ultérieure. Ils seront appelés au moment opportun dans le cycle de vie du composant. Cette heure peut survenir immédiatement après le montage du composant (par exemple, lorsque la méthode de cycle de vie componentDidMount est appelée ), pendant la phase Commit (méthode componentDidUpdate ), juste avant que le composant ne soit démonté ( componentWillUnmount ).



Remarquez qu'il existe trois méthodes de cycle de vie des composants associées à un hook? Le point ici est que les hooks vous permettent de combiner la logique associée, et non de la «disposer», comme elle l'était avant eux, selon différentes méthodes du cycle de vie des composants.



De nombreux composants doivent effectuer une action pendant leur montage, quelque chose doit être mis à jour chaque fois que le composant est redessiné, les ressources doivent être libérées immédiatement avant de démonter le composant pour éviter les fuites de mémoire. Grâce à l'utilisation, useEffecttoutes ces tâches peuvent être résolues en une seule fonction, sans diviser leur solution en 3 méthodes différentes, sans mélanger leur code avec le code d'autres tâches qui ne leur sont pas liées, mais qui nécessitent également ces méthodes.



Voici ce que les hooks React nous donnent:



  • Ils vous permettent de créer des composants qui sont représentés comme des fonctions plutôt que comme des classes.
  • Ils vous aident à mieux organiser votre code.
  • Ils facilitent le partage de la même logique entre différents composants.
  • De nouveaux hooks peuvent être créés en composant des hooks existants (en les appelant à partir d'autres hooks).


En général, nous vous recommandons d'utiliser des composants fonctionnels et des hooks plutôt que des composants basés sur des classes. Les composants fonctionnels sont généralement plus compacts que les composants basés sur des classes. Leur code est mieux organisé, plus lisible, plus réutilisable et plus facile à tester.



Composants de conteneur et composants de présentation



Dans un effort pour améliorer la modularité des composants et leur réutilisabilité, je me concentre sur le développement de deux types de composants:



  • Les composants de conteneur sont des composants qui sont connectés à des sources de données et peuvent avoir des effets secondaires.
  • Les beans de présentation sont, pour la plupart, de purs beans qui, étant donné les mêmes accessoires et le même contexte, retournent toujours le même JSX.


Les composants purs ne doivent pas être confondus avec la classe de base React.PureComponent , qui est ainsi nommée car elle n'est pas sûre à utiliser pour créer des composants qui ne sont pas purs.



▍Composants de présentation



Tenez compte des caractéristiques des composants de présentation:



  • Ils n'interagissent pas avec les ressources du réseau.
  • Ils n'enregistrent localStorageni ne chargent de données à partir de là.
  • Ils ne donnent pas de données imprévisibles.
  • Ils ne font pas directement référence à l'heure système actuelle (par exemple, en appelant une méthode Date.now()).
  • Ils n'interagissent pas directement avec le magasin d'état de l'application.
  • - , , , .


C'est à cause du dernier élément de cette liste que j'ai mentionné, en parlant des composants de présentation, que ce sont pour la plupart des composants purs. Ces composants lisent leur état à partir de l'état global de React. Par conséquent, comme les crochets useStateet useReducerleur fournir une certaine donnée implicite (c'est-à-dire - des données qui ne sont pas décrites dans la fonction de signature) qui, d'un point de vue technique, ne peuvent pas appeler de tels composants sont «propres». Si vous en avez besoin pour être vraiment propres, vous pouvez déléguer toutes les tâches de gestion de l'état au composant conteneur, mais je suppose que vous ne devriez pas le faire, au moins jusqu'à ce que le bon fonctionnement du composant puisse être vérifié à l'aide de modular des tests.



Le meilleur est l'ennemi du bien.



Voltaire




▍Composants du conteneur



Les composants de conteneur sont les composants responsables de la gestion de l'état, de l'exécution des opérations d'E / S ou de toute autre tâche pouvant être un effet secondaire. Ils ne doivent rendre aucun balisage par eux-mêmes. Au lieu de cela, ils délèguent la tâche de rendu aux composants de présentation et ils servent eux-mêmes de wrapper pour ces composants. En règle générale, un composant conteneur dans une application React + Redux appelle simplement mapStateToProps(), mapDispatchToProps()puis transmet les données appropriées aux composants de présentation. Les conteneurs peuvent également être utilisés pour certaines tâches générales, dont nous parlerons ci-dessous.



Composants d'ordre supérieur



Un composant d'ordre supérieur (HOC) est un composant qui prend d'autres composants et renvoie un nouveau composant qui implémente de nouvelles fonctionnalités basées sur les composants d'origine.



Les composants d'ordre supérieur fonctionnent en enveloppant certains composants avec d'autres. Un composant wrapper peut implémenter une logique et créer des éléments DOM. Il peut ou non transmettre des accessoires supplémentaires au composant enveloppé.



Contrairement aux hooks React et aux accessoires de rendu, les composants d'ordre supérieur se prêtent à la composition en utilisant l'approche standard de la composition fonctionnelle. Cela vous permet de décrire de manière déclarative les résultats de la composition des fonctionnalités destinées à être utilisées à différents endroits de l'application. Dans le même temps, les composants prêts à l'emploi ne doivent pas être conscients de l'existence de certaines possibilités. Voici un exemple de HOC d' EricElliottJS.com :



import { compose } from 'lodash/fp';
import withFeatures from './with-features';
import withEnv from './with-env';
import withLoader from './with-loader';
import withCoupon from './with-coupon';
import withLayout from './with-layout';
import withAuth from './with-auth';
import { withRouter } from 'next/router';
import withMagicLink from '../features/ethereum-authentication/with-magic-link';

export default compose(
  withEnv,
  withAuth,
  withLoader,
  withLayout({ showFooter: true }),
  withFeatures,
  withRouter,
  withCoupon,
  withMagicLink,
);


Voici un mélange de nombreuses fonctionnalités partagées sur toutes les pages du site. À savoir, il withEnvlit les paramètres des variables d'environnement, withAuthimplémente le mécanisme d'authentification GitHub, withLoaderaffiche une animation lors du chargement des données utilisateur, , withLayout({ showFooter: true })affiche une mise en page standard avec un pied de page, withFeatureaffiche les paramètres, withRoutercharge le routeur, withCouponest responsable du travail avec les coupons et withMagicLingprend en charge l'authentification des utilisateurs sans mot de passe à l'aide de De la magie .



En passant, étant donné que l'authentification par mot de passe est obsolète et constitue une pratique dangereuse, il vaut la peine d'utiliser d'autres méthodes d'authentification des utilisateurs de nos jours.



Presque toutes les pages du site susmentionné bénéficient de toutes ces fonctionnalités. Étant donné qu'ils sont composés par un composant d'ordre supérieur, vous pouvez tous les inclure dans un composant conteneur avec une seule ligne de code. Par exemple, voici à quoi cela ressemblerait pour la page du didacticiel:



import LessonPage from '../features/lesson-pages/lesson-page.js';
import pageHOC from '../hocs/page-hoc.js';
export default pageHOC(LessonPage);


Ces composants d'ordre supérieur ont une alternative, mais il s'agit d'une construction douteuse appelée «pyramide de malheur» et il vaut mieux ne pas l'utiliser. Voici à quoi ça ressemble:



import FeatureProvider from '../providers/feature-provider';
import EnvProvider from '../providers/env-provider';
import LoaderProvider from '../providers/loader-provider';
import CouponProvider from '../providers/coupon-provider';
import LayoutProvider from '../providers/layout-provider';
import AuthProvider from '../providers/auth-provider';
import RouterProvider from '../providers/RouterProvider';
import MagicLinkProvider from '../providers/magic-link-provider';
import PageComponent from './page-container';

const WrappedComponent = (...props) => (
  <EnvProvider { ...props }>
    <AuthProvider>
      <LoaderProvider>
        <LayoutProvider showFooter={ true }>
          <FeatureProvider>
            <RouterProvider>
              <CouponProvider>
                <MagicLinkProvider>
                  <YourPageComponent />
                </MagicLinkProvider>
              </CouponProvider>
            </RouterProvider>
          </FeatureProvider>
        </LayoutProvider>
      </LoaderProvider>
    </AuthProvider>
  </EnvProvider>
);


Et cela devra être répété sur chaque page. Et si quelque chose doit être changé dans cette structure, alors des changements devront y être apportés partout où il est présent. Je pense que les inconvénients de cette approche sont assez évidents.



L'utilisation de la composition pour résoudre des problèmes généraux est l'un des meilleurs moyens de réduire la complexité du code de votre application. La composition est si importante que j'ai même écrit un livre à ce sujet .



Résultat



  • Pourquoi réagir? React nous donne un rendu déterministe des visuels des composants, basé sur la liaison de données unidirectionnelle et l'état immuable du composant.
  • JSX nous permet de décrire facilement de manière déclarative les interfaces en code JavaScript.
  • - .
  • . , . , DOM DOM.
  • React , . , , .
  • - . - .
  • , . ( , ).


?



Dans cet article React, nous avons couvert de nombreux concepts de programmation fonctionnelle. Si vous recherchez une compréhension approfondie des principes du développement d'applications React, il vous sera utile de parfaire vos connaissances sur les fonctions pures , sur l' immuabilité , sur le currying et l'application partielle des fonctions, sur la composition des fonctions. Vous pouvez trouver des documents connexes sur EricElliottJS.com .



Je recommande d'utiliser React en conjonction avec Redux , Redux-Saga et RITEway . Redux est recommandé pour une utilisation avec Autodux et Immer... Pour organiser des schémas d'états complexes, vous pouvez essayer d'utiliser Redux-DSM .



Lorsque vous maîtrisez les bases et que vous êtes prêt à créer de vraies applications React, jetez un œil à Next.js et Vercel . Ces outils aideront à automatiser la configuration du système de génération de projet et du pipeline CI / CD, avec leur aide, vous pouvez préparer le projet pour un déploiement optimisé sur le serveur. Ils ont le même effet que toute une équipe DevOps, mais ils sont totalement gratuits.



Quels outils auxiliaires utilisez-vous lors du développement d'applications React?










All Articles