La plupart des personnes travaillant dans le frontend, d'une manière ou d'une autre, ont rencontré une réaction. Il s'agit d'une bibliothèque JavaScript qui aide à créer des interfaces sympas; ces dernières années, elle a gagné en popularité. Cependant, peu de gens savent comment cela fonctionne à l'intérieur.
Dans cette série d'articles, nous lisons le code et essayons de comprendre de quoi sont responsables les paquets qui se trouvent sous le capot de React, à quoi ils servent et comment ils fonctionnent. Les plus basiques que nous utilisons dans le navigateur sont react
, react-dom
, events
et react-reconciler
.
Nous allons avancer dans l'ordre et nous avons aujourd'hui un article sur le paquet react
. Peu importe ce qui est dans ce paquet - passez sous le chat.
Tout d'abord, nous allons faire un petit exemple, sur la base duquel nous considérerons ce paquet. Notre gadget ressemblera à ceci:
function App() { const [text, changeText] = React.useState('Initial'); return ( <div className="app"> <span>{text}</span> <input type="text" value={text} onInput={(e) => changeText(e.target.value)} /> </div> ); } ReactDOM.render( <App />, document.getElementById('root') ) ;
Jetons un coup d'œil à ce morceau de code. Ici, nous voyons l'appel du hook via React.useState('Initial')
, un peu de JSX et l'appel de la méthode de rendu pour obtenir tout cela sur la page.
En fait, comme beaucoup de gens le savent, ce n'est pas le code final que le navigateur traite. Avant d'être exécuté, il est transpilé, par exemple, avec un babel. Dans ce cas, ce que la fonction retourne se transformera en ce qui suit:
return React.createElement( "div", { className: "app" }, React.createElement("span", null, text), React.createElement("input", { type: "text", value: text, onInput: function onInput(e) { return changeText(e.target.value); } }) );
Qui se soucie d'expérimenter et de voir en quoi votre code babel se transforme - babel repl .
React.createElement
Nous avons donc eu beaucoup d'appels à React.createElement()
et du temps pour voir ce que fait cette fonction. Nous le décrirons en mots (ou vous pouvez également consulter le fichier - ReactElement.js ).
Tout d'abord, il vérifie si nous avons des accessoires (dans le code, l'objet avec des accessoires que nous avons passé s'appelle config
).
Ensuite, nous vérifions si nous avons key
accessoires key
et ref
qui ne sont pas undefined
, et les enregistrons, le cas échéant.
if (hasValidKey(config)) { key = '' + config.key; }
Un point intéressant est que config.key
est config.key
chaîne, ce qui signifie que vous pouvez passer n'importe quel type de données sous forme de clé, l'essentiel est qu'il implémente la .toString()
ou .valueOf()
et renvoie une valeur unique à un ensemble particulier.
Voici les étapes suivantes:
- copier les accessoires qui ont été transmis à l'élément;
- ajoutez-y le champ
children
si nous les avons passés non pas avec des accessoires, mais comme élément imbriqué; - nous définissons les valeurs par défaut de
defaultProps
pour les propriétés que nous n'avons pas defaultProps
précédemment.
Lorsque nous avons préparé toutes les données, nous appelons une fonction interne qui crée un objet qui décrit notre composant. Cet objet ressemble à ceci:
{ // This tag allows us to uniquely identify this as a React Element $$typeof: REACT_ELEMENT_TYPE, // Symbol // Built-in properties that belong on the element type: type, key: key, ref: ref, props: props, // Record the component responsible for creating this element. _owner: owner, }
Ici, nous avons la propriété $$typeof
, qui est un symbole, alors glissez de toute façon quel objet échouera.
La propriété type
stocke le type de l'élément à créer. Dans le cas de notre exemple, ce sera la fonction App()
et les lignes 'div'
, 'span'
et 'input'
.
La propriété key
contiendra la même clé, à cause de laquelle les warings volent vers la console.
Les accessoires contiendront ce que nous avons transmis, les children
et ce qui a été spécifié dans defaultProps
. La propriété _owner
nécessaire pour un travail correct avec ref
.
Traduit dans notre exemple, le résultat de React.createElement(App, null)
ressemblera à ceci:
{ $$typeof: REACT_ELEMENT_TYPE, type: App, key: null, ref: null, props: {}, _owner: null, }
De plus, en mode dev, nous aurons un champ supplémentaire qui sera utilisé pour afficher une belle pile avec le nom et la ligne du fichier:
_source: { fileName: "/Users/appleseed/react-example/src/index.js", lineNumber: 7 }

Pour résumer un peu ce que nous avons vu ci-dessus. Le package react
agit comme un traducteur entre nous et le reste des packages qui travaillent plus loin sur notre application, traduisant nos appels en mots compréhensibles, par exemple, pour le réconciliateur.
React.useState
Dans la version 16.8, des crochets sont apparus. Qu'est-ce que c'est et comment l'utiliser, vous pouvez lire le lien , mais maintenant nous regardons ce qui se trouve dans le paquet react
.
En fait, il n'y a pas grand-chose à dire. En substance, un package est une façade à travers laquelle nos défis vont aux entités internes.
Ainsi, useState
n'est rien de plus que deux lignes de code:
export function useState<S>(initialState: (() => S) | S) { const dispatcher = resolveDispatcher(); return dispatcher.useState(initialState); }
Les crochets restants sont presque identiques. Ici, nous obtenons le répartiteur actuel, qui est un objet et contient des champs, par exemple useState
. Ce répartiteur change selon que nous avons le premier rendu maintenant ou que nous voulons simplement mettre à jour le composant.
L'implémentation réelle des hooks est stockée dans le package react-reconciler
, dont nous parlerons dans l'un des articles suivants.
Et ensuite
Encore une chose. Après avoir lu cet article, vous pouvez comprendre pourquoi nous importons toujours le package react, même si nous ne l'utilisons pas directement. Ceci est nécessaire pour qu'après avoir digéré notre jsx par la bulle, nous ayons une variable React
.
Les gars de l'équipe createElement
pris soin de cela (et pas seulement) et travaillent maintenant à remplacer createElement
.
Essayer d'expliquer en un mot: il y a un désir de remplacer la méthode actuelle de création d'éléments par deux - jsx
et jsxs
. Cela est nécessaire pour plusieurs raisons:
- nous avons discuté ci-dessus comment fonctionne
createElement
. Il copie constamment les accessoires et ajoute le champ children
à l'objet, dans lequel il enregistre les enfants que nous avons passés comme arguments à la fonction (3 arguments et plus). Il est maintenant proposé de le faire au stade de la conversion des jsx
en javascript
, car la création d'un élément est une fonction souvent appelée et il n'est pas libre d'effectuer à chaque fois la modification des accessoires lors de l'exécution; - vous pouvez vous débarrasser de l'importation de l'objet
React
et importer uniquement des fonctions spécifiques ( import { jsx } from 'react'
, par exemple) et, en conséquence, ne pas pouvoir ajouter à l'assembly ce que nous n'utilisons pas. De plus, vous n'avez pas à résoudre le champ createElement
de l'objet React
chaque fois, car il n'est pas non plus libre; - Nous avons discuté ci-dessus que nous avons un cas particulier lorsque nous retirons la
key
des accessoires et la faisons avancer. Il est maintenant proposé de prendre la key
de jsx
à l'étape de transpiling et de lui passer le troisième paramètre à la fonction de création d'élément.
Lisez plus ici . Le package jsx
possède déjà les jsxs
jsx
et jsxs
. Si vous souhaitez jouer avec cela, vous pouvez cloner le référentiel enableJSXTransformAPI
, définir l'indicateur enableJSXTransformAPI
sur true
dans le fichier ReactFeatureFlags.js
du package shared
et compiler votre version de react ( yarn build
) avec la nouvelle API activée.
Finale
À ce sujet, je terminerai l'histoire d'aujourd'hui sur le package react
et la prochaine fois nous parlerons de la façon dont le package react-dom
utilise ce que react
crée, et quelles méthodes et comment il implémente.
Merci d'avoir lu jusqu'au bout!