Comment React fonctionne sous le capot

React est une bibliothèque JavaScript très populaire. Avec plus de 5,5 millions de téléchargements hebdomadaires, React jouit d'une grande popularité. Mais peu de développeurs React savent comment React fonctionne sous le capot.

Dans cet article, je vais essayer de découvrir des choses intéressantes sur React que vous, en tant que développeur React, pourriez trouver fascinantes. Commençons par le début.

Mais avant de commencer, si vous êtes un développeur React, j'ai des nouvelles intéressantes pour vous! Une fois cet article terminé, vous pourrez développer quelque chose de cool avec React et gagner des prix en cours de route :)

Que fait React?

À la base, React maintient essentiellement un arbre pour vous. Cet arbre est capable de faire des calculs différentiels efficaces sur les nœuds.

Considérez votre code HTML comme un arbre. En fait, c'est exactement ainsi que le navigateur traite votre DOM (votre HTML rendu sur le navigateur). React vous permet de reconstruire efficacement votre DOM en JavaScript et de ne transmettre que les modifications qui se sont réellement produites dans le DOM.

JSX est un sucre syntaxique

Il n'y a rien de tel que JSX - ni pour JavaScript, ni pour le navigateur. JSX est simplement du sucre syntaxique pour créer des objets JavaScript très spécifiques.

Lorsque vous écrivez quelque chose comme:

const tag = 

Hello

ce que vous faites essentiellement est ceci:

const tag = React.createElement("h1", {}, "Hello")

Vous voyez, lorsque vous commencez à écrire des éléments imbriqués, non seulement c'est difficile à coder, mais cela devient également très gênant de maintenir une telle base de code. JSX vous aide ainsi à apporter la propreté du HTML à la puissance de JavaScript.

Mais que fait React.createElement lui-même? Il crée un vieil objet JavaScript simple. En fait, vous pouvez l'appeler manuellement et voir par vous-même!

Vous voyez, nous avons un objet comme celui-ci:

{ $$typeof: Symbol(react.element), key: null, props: {children: "Hello"}, ref: null, type: "div" }

Et si nous commençons à imbriquer des éléments comme celui-ci:

React.createElement('div', { }, React.createElement('p', {}, 'A p inside a div') ) 

Nous commencerions à obtenir des objets imbriqués:

Alors maintenant, vous savez, une fois que tout le JSX est analysé et que tous les appels React.createElement ont été résolus, nous atterrissons avec un objet imbriqué géant comme ci-dessus.

Rendu de réaction

Maintenant, si vous revenez au point où nous démarrons notre application, vous verrez que dans votre fichier index.js, vous trouverez la ligne suivante:

// .. prev code ReactDOM.render(, container)

D'en haut, nous savons que lorsque l' analyse est terminée, il ne s'agit que d'un énorme objet d'éléments React. Alors comment React est-il capable de construire de véritables balises div et p? Rencontrez ReactDOM.

ReactDOM à son tour, crée de manière récursive des nœuds en fonction de leur propriété 'type' et les ajoute finalement au DOM.

Il devrait être clair à ce stade que le découplage de React des moteurs de rendu est en fait une bonne chose! Ce que fait React, c'est simplement construire une arborescence d'interface utilisateur qui pourrait être utilisée non seulement sur le Web, mais aussi sur des environnements comme les mobiles, étant donné qu'un moteur de rendu est disponible, capable de communiquer avec le système d'exploitation hôte. Ici, React Native vient jouer. Vous voyez, React Native utilise la bibliothèque React, mais pas ReactDOM comme rendu. Au lieu de cela, le package react-native lui-même est un moteur de rendu.

Nous faisons cela dans une application native react pour démarrer l'application:

const { AppRegistry } = require('react-native') AppRegistry.registerComponent('app', () => MainComponent)

Regardez! Pas de ReactDOM. Pourquoi pas? Parce que nous n'avons pas de méthodes comme appendChild, nous n'avons pas non plus d'environnement de type DOM. Au lieu de cela, pour les mobiles, nous avons besoin d'une prise en charge de l'interface utilisateur directement à partir du système d'exploitation. Mais la bibliothèque React n'a pas besoin de savoir cela, le moteur de rendu (React Native) s'en charge.

Réagir la réconciliation

Quand nous disons que React maintient une copie du DOM en utilisant le DOM virtuel en JavaScript, et qu'il l'utilise pour le différencier de tout changement et l'appliquer à un vrai DOM, nous ne voulons pas que React force brutalement son chemin. Réagissez, en fait fait une réconciliation très paresseuse. React ferait le moins de changements possible, c'est-à-dire qu'il essaierait de réutiliser des éléments, des attributs et même des styles si possible!

Prenons cet exemple:

stuff

Disons que vous changez cette expression JSX en celle ci-dessous en utilisant une condition ou un état:

something else

Maintenant, tout en différant, React verrait bien cela, la balise img utilise le même nom de classe dans les anciens et les nouveaux arbres, alors pourquoi le modifier. Et cela modifierait simplement votre attribut alt et continuerait.

Cependant, il y a un hic. Parce que nous ne voulons pas que React fasse beaucoup de calculs sur des parties différentes, React supposerait que si un parent a changé, son sous-arbre contenant a définitivement changé. Par exemple:

I did not change

Si vous modifiez ce JSX en utilisant la condition / l'état ci-dessous:

I did not change

Bien que vous puissiez voir que nous n'avons pas besoin de recréer la balise p interne, mais React n'a aucun moyen de savoir que tout en parcourant l'arbre à partir du haut (à moins, bien sûr, que vous n'effectuiez de lourdes différences d'arbres, qui sont des algorithmes beaucoup plus coûteux que l'heuristique O (n) react suit pour diffing). Ainsi, React décide de détruire tous les enfants (c'est-à-dire en appelant leurs fonctions de nettoyage dans useEffect, ou componentWillUnmount dans les composants basés sur les classes) et de recréer les enfants à partir de zéro.

Clés de réaction

Lors de l'ajout / suppression d'éléments dans un nœud, React ferait simplement une boucle sur les enfants dans l'ancien arbre et les enfants dans la nouvelle arborescence du nœud et marquerait les endroits où il a besoin d'effectuer tout ajout / suppression. Mais cela a un inconvénient sans l'aide supplémentaire du développeur. Prenons cet exemple:

  • A
  • B

Considérez que ceci est changé en ci-dessous par condition / état:

  • Z
  • A
  • B

Maintenant, quand React commencerait à comparer les deux listes pour la différence, il trouverait la différence au nœud enfant 1, ferait muter l'ancien A en nouveau Z, puis à nouveau au nœud enfant 2, le ferait muter de l'ancien B au nouveau A, puis enfin ajouter le nouveau nœud B.

Cependant, un meilleur moyen aurait été de préserver les nœuds A et B existants et de simplement ajouter le nœud Z. Mais comment React en serait-il informé? Les clés de réaction aideraient.

Les clés fournissent juste un bon moyen de réagir pour savoir quels éléments ont changé ou pas changé pendant qu'ils différaient. Maintenant, au lieu de comparer l'élément entier, React comparerait les clés des enfants pour voir quel élément doit être ajouté / supprimé. La méthode ci-dessous est un moyen efficace d'effectuer la même chose:

  • A
  • B

Maintenant, si cela est changé en:

  • Z
  • A
  • B

React saurait maintenant que les clés 'A' et 'B' existent déjà, il suffit donc d'ajouter le nouvel élément avec la clé 'Z'.

Êtes-vous un développeur React? Montrez vos compétences React en développant un jeu interactif de 3 minutes dans React et gagnez des sweats à capuche, des chemises et des tasses à café ! Participez à codecomp en rejoignant le serveur discord de codedamn ici

Voilà donc quelques concepts importants qui, je pense, vous seraient vraiment utiles en tant que développeurs React pour commencer à comprendre le cœur de React et son fonctionnement réel. N'hésitez pas à transmettre vos suggestions ou questions à propos du même sujet.

Vous pouvez me suivre sur Twitter pour plus de tweets JS / codage et autres. Paix!