• 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 15/12/2020

Décrivez un composant avec JSX

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Nous avons vu qu’il est possible de décrire nos DOM virtuels React (« grappes ») avec la fonction  React.createElement(…) . Nous allons voir dans ce chapitre qu'il est préférable d'utiliser la syntaxe JSX, créée spécifiquement pour React.

Pourquoi préférer JSX ?

Lorsqu’on se limite à un petit exemple, JSX ne semble pas être d'une énorme utilité…

// Avec JSX
<p>Oh le joli paragraphe</p>
// sans JSX
React.createElement(p, {}, 'Oh le joli paragraphe')

Ou encore :

// Avec JSX
<User first="John" last="Smith" />
// sans JSX
React.createElement(User, { first: 'John', last: 'Smith' })

Mais très vite, il devient difficile de se repérer. Jugez plutôt avec cette grappe pourtant assez simple :

<form method="post" action="/sessions" onSubmit={this.handleSubmit}>
<p className="field">
<label>
E-mail
<input
type="email"
name="email"
required
autoFocus
value={this.state.email}
onChange={this.handleFieldChange}
/>
</label>
</p>
<p className="field">
<label>
Mot de passe
<input
type="password"
name="password"
required
value={this.state.password}
onChange={this.handleFieldChange}
/>
</label>
</p>
<p>
<button type="submit" value="Connexion" />
</p>
</form>

Remarquez comme il est facile de repérer  la connexion aux traitements associés (handleSubmit ,  handleFieldChange), la source des données (this.state.email,  this.state.password), ainsi que la structure générale du composant.

Voyons maintenant l’équivalent sans JSX :

React.createElement(
'form',
{ method: 'post', action: '/sessions', onSubmit: this.handleSubmit },
React.createElement(
'p',
{ className: 'field' },
React.createElement(
'label',
null,
'E-mail',
React.createElement('input', {
type: 'email',
name: 'email',
required: true,
autoFocus: true,
value: this.state.email,
onChange: this.handleFieldChange,
})
)
),
React.createElement(
'p',
{ className: 'field' },
React.createElement(
'label',
null,
'Mot de passe',
React.createElement('input', {
type: 'password',
name: 'password',
required: true,
value: this.state.password,
onChange: this.handleFieldChange,
})
)
),
React.createElement(
'p',
null,
React.createElement('button', { type: 'submit', value: 'Connexion' })
)
)

Voyez comme ces mêmes informations sont noyées dans la masse de code, alors même que notre grappe reste modeste. Imaginez ce que ça donne pour un composant un tant soit peu complexe.

JSX, c’est bien plus que du simple confort : c’est une barrière en moins pour la compréhension rapide du fonctionnement de nos composants lorsqu’on lit le code. C’est de la maintenabilité - et de la productivité ! – en plus.

Ni du HTML, ni du XML

Dans la mesure où JSX est un langage à balises, il est tentant pour les développeurs de conserver tous leurs réflexes et modèles mentaux hérités de HTML ou d’XML. En pratique, si des similitudes existent, JSX présente aussi des différences importantes.

Parmi les points communs avec XML, on citera notamment :

  • La sensibilité à la casse : les majuscules et minuscules ne sont pas interchangeables,  <CoolComponent />  n’a pas le même sens que  <coolComponent />  .

  • L’exigence de fermeture des éléments :

    1. même pour les éléments « seuls », comme par exemple  <input />  (et non pas  <input> ),

    2. dans le bon ordre (on ferme dans l’ordre inverse de l'ouverture) :  <p><span>Bien</span></p>  mais pas  <p><span>Pas bien</p></span>  .

En revanche, pour le reste, il va falloir ajuster ses habitudes…

Valeurs des props

En JSX, on ne parle pas d’attributs (comme en XML ou HTML), mais de props. Ce terme revient dans toute la documentation et l’écosystème React.

String vs. tout le reste

En pratique, une prop peut avoir n’importe quelle valeur possible en JavaScript, mais syntaxiquement, dans JSX, on n’a en gros que deux possibilités : un littéral  String , matérialisé par les double-quotes ""  ou une expression JSX, définie entre accolades{}.

<input
type="email"
name="email"
maxlength={42}
readonly={false}
onChange={this.handleFieldChange}
value={this.state.value}
/>

Ici, les props  type  et  name  ont des valeurs de type  String . La syntaxe « façon HTML » s’applique donc. En XML ou en HTML il serait possible d'écrire, par exemple, maxlength=42 ( ou  maxlength="42" ).  En JSX, il est par contre obligatoire d'utiliser une expression JSX (comme  maxlength={42} , pour notre exemple) pour toute valeur qui n'est pas de type  String  .

En revanche, le type réel de l’expression JSX est préservé : la prop que l'on récupèrera dans le composant sera bien, par exemple, un nombre, un booléen, un tableau, un autre composant, etc.

Raccourci pour true

JSX garde un raccourci syntaxique classique en HTML, pour les props booléennes dont la valeur est  true . Au lieu d’écrire explicitement la valeur dans une expression JSX, on peut se contenter du nom de la prop, comme ceci :

<input type="email" name="email" autoFocus required />

Cette syntaxe courte est d’ailleurs recommandée.

Mots réservés

Dans la mesure où JSX produit, au final, du code JavaScript, et que celui-ci doit pouvoir s’exécuter dans un environnement ES5 (par exemple Internet Explorer 9+ ou d'anciennes versions de Node.js) et non ES2015+ (navigateurs modernes, versions de Node.js récentes, etc.), il n’est pas possible d’utiliser des mots-clés de JavaScript comme noms de props.

On retrouve donc les mêmes ajustements que dans les interfaces du DOM, qui reviennent essentiellement à écrire  className  au lieu de  class  et  htmlFor  au lieu de  for  .

Commentaires

Contrairement à HTML et XML, JSX n’a pas de syntaxe dédiée pour les commentaires (par exemple  <!-- … --> ) : si vous souhaitez mettre des commentaires au sein d’une grappe JSX, il faut le faire au sein d’une expression. Ça peut sembler parfois un peu chargé :

<form method="post" action="/sessions" onSubmit={this.handleSubmit}>
{/* La classe 'field' assure l’espacement vertical convenable */}
<p className="field">
<label>
E-mail
<input type="email" name="email" required autoFocus
value={this.state.email}
{/*
Avec les champs contrôlés, il est indispensable de fournir `onChange`
pour éviter que le champ soit fourni en lecture seule au niveau du
DOM.
*/}
onChange={this.handleFieldChange}
/>
</label>
</p>
<p><button type="submit" value="Connexion" /></p>
</form>

L’importance de la casse

On l’a dit, JSX est sensible à la casse (Majuscules / minuscules), pour ses noms d’éléments comme pour ses noms de props  (par exemple,  autoFocus  n’est pas la même prop que  autofocus , qui d'ailleurs n'existe pas).

Pour les noms d’éléments, c’est encore plus important :

  • dans une grappe JSX, si un élément démarre par une minuscule, le moteur considère qu’il s’agit d’un élément natif fourni par la plate-forme (le navigateur, par exemple),

  • alors que si l’élément démarre par une majuscule, on estime qu’il s’agit d’un composant React.

Le code JavaScript obtenu ne sera donc pas le même :

[
<CoolComponent/>,
<coolComponent/>,
]
// donne :
[
React.createElement(CoolComponent, null),
React.createElement('coolComponent', null),
]

Notez que dans le second cas, on produit juste une  String  qui donne le nom de la balise supposée native, alors que dans le premier cas, on référence bien le composant React supposé (classe ou fonction).

Mise en application

Pour pratiquer un peu JSX, nous allons ajuster notre application.

Récupérer la base de travail

Nous allons commencer par supprimer certains fichiers inutiles pour le moment dans  src/  :  App.test.js  ,  index.css  et  logo.svg . Ensuite, nous reprendrons des squelettes de travail à partir du dépôt public pour ce projet, disponible sur GitHub, afin de pouvoir nous concentrer sur la partie React.

  1. Allez sur le dépôt GitHub, à l’étiquette debut-jsx (cliquez sur le lien)

  2. Récupérez les feuilles de style  App.css  ,  Card.css  et  GuessCount.css , ainsi que les squelettes de Card.js  et  GuessCount.js. Puis posez-les dans votre dossier src/.

  3. Vous pouvez alors soit apurer votre  App.js  pour être identique à celui de départ sur GitHub, soit y récupérer directement  App.js  comme base de travail.

  4. Vous pouvez aussi en profiter pour changer le  <title>  de  public/index.html  de « React App » vers le plus représentatif « React Memory ».

Écrire le composant Carte

Nous faisons un jeu de Memory. Le composant de base qui sera affiché est donc la carte. Elle devra être soit visible, soit présentée « de dos », masquée. Partons du principe que notre composant  <Card />  recevra deux props :  card  , qui sera le symbole à afficher ; et  feedback  , qui indiquera l’état visuel de la carte : masquée ou visible. Voici comment ajuster la fonction  Card  :

const Card = ({ card, feedback }) => (
<div className={`card ${feedback}`}>
<span className="symbol">
{feedback === 'hidden' ? HIDDEN_SYMBOL : card}
</span>
</div>
)

Remarquez quelques points :

  • On déstructure directement les props passées en arguments (un gros objet de props).

  • Étant donné que la fonction renvoie directement une grappe de DOM virtuel, sans calcul préalable, on se dispense des accolades de bloc et du  return  , pour renvoyer directement l’expression : on trouve donc plutôt des parenthèses autour du JSX.

  • La syntaxe de valeur textuelle pour une prop ne permet pas l’interpolation (c'est-à-dire l’incrustation de contenu dynamique dans le texte). On a donc recours pour le   <div>  principal à une expression JSX (entre accolades) qui, elle, peut utiliser la syntaxe des template strings d’ES2015, entre backquotes (  `  ), pour incruster dynamiquement la valeur de  feedback  .

Écrire le composant Compteur de tentatives

Le composant  <GuessCount />  est très simple : c’est un compteur de tentatives. Au fil de la partie, on l’affichera avant le plateau de cartes. Il attend juste une prop nommée  guesses . Voici le code final :

const GuessCount = ({ guesses }) => <div className="guesses">{guesses}</div>

Écrire le composant Application

Le composant principal reste  <App /> . Il est défini par une classe et non par une simple fonction, sans raison particulière pour le moment, mais ça viendra : nous en verrons les détails dans la prochaine partie de ce cours.

Pour l'instant, nous allons y poser le compteur et une série de six cartes « en dur » :

import React from 'react';
import Card from './Card'
import GuessCount from './GuessCount'
class App extends React.Component {
render() {
return (
<div className="memory">
<GuessCount guesses={0} />
<Card card="😀" feedback="hidden" />
<Card card="🎉" feedback="justMatched" />
<Card card="💖" feedback="justMismatched" />
<Card card="🎩" feedback="visible" />
<Card card="🐶" feedback="hidden" />
<Card card="🐱" feedback="justMatched" />
</div>
)
}
}

Remarquez qu'il y a 4 feedbacks (ou états visuels) possibles et qui sont donc prévus :

  • La carte est masquée (  hidden  ).

  • La carte fait partie de la tentative en cours, qui vient de réussir une paire (  justMatched  ).

  • La carte fait partie de la tentative en cours, qui vient de rater une paire (  justMismatched  ).

  • La carte appartient à une paire précédemment réussie (  visible  ).

Exemple de certificat de réussite
Exemple de certificat de réussite