Le JSX ou JavaScript eXtension Langage est une extension de langage développée par les équipes de Facebook lorsqu’ils ont créé la librairie React. C’est un sucre syntaxique qui permet aux développeurs d’écrire le rendu de leur composant de manière plus lisible.
“Ok, très bien mais je ne suis pas sûr de comprendre cette définition”. On va s’appuyer sur un exemple pour mieux appréhender ce langage.
Un composant React
Lorsqu’on lit un composant React, on a l’impression de prime abord d’écrire le rendu de notre composant en HTML. Prenons l’exemple suivant :
import "./styles.css";
export default function App() {
return (
<div className="App">
<h1>Hello Beni</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
et son rendu :
On imagine que la balise <div> ou encore les balises <h1> ou <h2> sont nos balises HTML que l’on a l’habitude de voir dans un DOM.
Même sur le rendu, si on affiche le code HTML de la page créée, on voit :
Finalement, c’est logique de se dire qu’on a écrit du HTML.
Cependant, je vous ai dit en introduction que le JSX est un sucre syntaxique et nous allons voir pourquoi on ne parle pas de HTML ici.
Comment React gère le rendu HTML ?
Avant toute chose, reprenons rapidement les bases en React. Le point d’entrée d’une application React est le fichier index.html.. Ce dernier ressemble plus ou moins à ça :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>
Il contient un Header et un Body comme toute page HTML. Cependant, dans notre body, on trouve seulement une div avec l’id root et rien d’autre…
React est une librairie permettant de créer des Single Page Applications (SPA), c’est-à-dire une seule page HTML dont le contenu se met à jour suite aux interactions des utilisateurs. Mais comment ça marche ?
Notre application contient un fichier index.jsx permettant de remplir le code HTML de la page dynamiquement :
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<App />
</StrictMode>
);
Le code suivant const rootElement = document.getElementById(“root”); sélectionne la div présente dans notre page HTML. La ligne suivante crée un rootElement dans lequel React va render l’application.
Derrière, React garde en mémoire une copie du DOM (appelée le DOM virtuel) pour ensuite calculer les différences entre le DOM virtuel et le DOM réel et mettre à jour uniquement les parties ayant été modifiées. Cette méthode permet d’optimiser les changements du DOM et d’avoir une vraie SPA.
Ecriture d’un composant sans JSX
On comprend maintenant comment React fait pour charger le code HTML dans notre index.html.
On peut écrire le rendu de notre composant sans utiliser le JSX et c’est ce que l’on va faire ici pour mieux comprendre son utilité.
React possède une méthode React.createElement(tagName[ ,options]); (documentation : https://developer.mozilla.org/fr/docs/Web/API/Document/createElement) permettant de créer un élément HTML du type spécifié par le tagName.
Si je reprends mon composant App plus haut pour l’écrire sans JSX, j’obtiens le code suivant :
import React from "react";
import "./styles.css";
export default function App() {
return React.createElement("div", { className: "App" }, [
React.createElement("h1", {}, "Hello Ben"),
React.createElement("h2", {}, "Start editing to see some magic happen!"),
]);
}
On retrouve notre élément div avec un className équivalent à App pour le style et deux éléments enfants qui sont notre titre h1 avec “Hello Ben” et notre titre h2.
Vous pouvez tester les deux snippets de code, vous retrouverez exactement le même rendu.
Si on se pose deux minutes pour comparer les deux manières de rédiger le rendu de notre composant, lequel préférez-vous ?
- Écrire du code qui ressemble à du HTML que tout développeur sait lire et écrire
- Utiliser des méthodes d’une librairie qu’on ne connaît pas et qu’il va falloir apprendre
Je ne sais pas vous mais mon choix est vite fait !
C’est la raison principale qui a poussé React à créer le JSX, ce sucre syntaxique améliorant l’expérience utilisateur pour une meilleure adoption de la librairie par la communauté.
React lit votre code JSX, le transforme en ce qu’il connaît, les méthodes createElement pour que ces dernières produisent le code HTML.
Une limitation liée au JSX
Tout développeur React a déjà vu cette erreur au moins une fois :
Adjacent JSX elements must be wrapped
Si j’essaie de créer un composant comme suit :
import "./styles.css";
export default function App() {
return (
<h1>Hello Beni</h1>
<h2>Start editing to see some magic happen!</h2>
);
}
React me crie dessus avec l’erreur suivante :
Si vous avez suivi le paragraphe précédent, vous comprenez pourquoi on a cette limitation. Si on essaie de l’écrire sans le JSX, cela donne :
export default function App() {
return React.createElement("h1", {}, "Hello Beni") +
React.createElement("h2", {}, "Start editing to see some magic happen!");
}
en précisant qu’on retourne deux éléments : un h1 plus un h2, mais cela n’est pas possible car un composant doit renvoyer un seul nœud principal.
Or, en HTML, le DOM que l’on manipule est un arbre avec des nœuds. On a forcément un nœud parent qui contient des enfants. Si on reprend notre index.html, on a un nœud parent HTML qui contient ensuite un nœud header et un nœud body. Le body contient ensuite un nœud div root qui correspond à notre application.
Pour la suite, en React, c’est pareil. Les composants que l’on crée suivent la même procédure. Mon composant App doit rendre un élément (un nœud) contenant d’autres composants (le h1 et le h2). Ma méthode de rendu ne peut pas retourner deux éléments et je ne peux pas non plus appeler la méthode createElement en donnant plus d’un tagName.
L’erreur remontée indique que l’on est en train d’enfreindre cette règle et que l’on doit wrapper nos deux éléments dans un élément de plus haut niveau, par exemple une <div>.
Cependant, si on regarde le rendu HTML, on aperçoit un nœud qui ne sert à rien :
<div id="root">
<div>
<h1>Hello Beni</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
</div>
On aurait préféré le DOM suivant :
<div id="root">
<h1>Hello Beni</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
Les Fragments à la rescousse
React a implémenté une solution au problème évoqué ci-dessus en introduisant les Fragments (documentation : https://react.dev/reference/react/Fragment) dans la version 16.2.0. Ces composants permettent de wrapper des composants sans créer de nœud englobant qui permet de contourner l’erreur.
Les Fragments peuvent être écrits de plusieurs façons et elles sont toutes égales :
- <></> (recommandée)
- <Fragment></Fragment>
- <React.Fragment></React.Fragment>
Le code devient dorénavant :
import "./styles.css";
export default function App() {
return (
<>
<h1>Hello Beni</h1>
<h2>Start editing to see some magic happen!</h2>
</>
);
}
pour un arbre en HTML sans aucun noeud supplémentaire :
Conclusion
Le JSX est un sucre syntaxique inventé par React pour améliorer l’expérience utilisateur lorsqu’on écrit le rendu de notre application au travers des composants.
On le sait bien : plus une librairie est facile d’utilisation, plus son acceptation par la communauté sera grande. Ainsi, React a introduit les Fragment pour contourner la restriction sur le fait qu’un composant va forcément render un nœud principal (arbre) en ajoutant la possibilité d’avoir un nœud qui ne sera pas rendu en HTML.
Dorénavant, vous comprenez mieux les choix de React dans la création d’un nouveau langage syntaxique et dans l’ajout de certaines fonctionnalités telles que les Fragments.
Par Benjamin Yvernault