Le hoisting au service des composants React

À première vue, le titre est un peu barbare. Pas d'inquiétude, nous allons commencer par découvrir ce qu'est le hoisting et dans un deuxième temps nous verrons quel peut être son intérêt pour les composants React.

Hissez haut ! Santiano 🎵

Pour découvrir le hoisting, ou "hissage" en français, nous allons partir du code suivant.

print("Il s'agirait de grandir")

function print(log) {
  console.log(log)
}

// Ce code affiche : "Il s'agirait de grandir"

Même si la fonction print est déclarée après avoir été appelée ce code fonctionne.

Cela vient du fait que Javascript va "remonter" les déclarations de fonctions en haut du code. Dans la réalité, le code reste à sa place, mais est stocké en mémoire afin de pouvoir être appelé partout.

Explication issue de la MDN.

Conceptuellement, par exemple, une définition stricte du hissage suggère que les déclarations de variables et de fonctions sont déplacées physiquement en haut de votre code, mais ce n'est pas ce qui se passe en fait. A la place, les déclarations de variables et de fonctions sont mises en mémoire pendant la phase de compilation, mais restent exactement là où vous les avez tapées dans votre code.

Transformons le code précédent en utilisant une fonction fléchée.

print("Il s'agirait de grandir")

const print = (log) => {
  console.log(log)
}

// Ce code affiche : "Uncaught ReferenceError: Cannot access 'print' before initialization"

Avec const et let la remonté a lieu mais pas l'initialisation. En d'autres termes appeler print ne générera pas une erreur du type print is not defined. En revanche, print n'a pas été initialisé, donc il correspond à rien.

Au sein de React

Je me suis longtemps demandé quelle syntaxe privilégier pour les composants fonctionnels.

const Button = ({ value, onClick }) => {
  return <button onClick={onClick}>{value}</button>
}

// ou

function Button({ value, onClick }) {
  return <button onClick={onClick}>{value}</button>
}

Étant donné que l'on a parlé du hoisting juste avant, vous vous doutez que la seconde forme a un avantage sur la première. On peut trouver un intérêt dans la déclaration des propTypes et des defaultProps.

Avec une fonction fléchée.

import React from "react"
import PropTypes from "prop-types"

const Button = ({ value, onClick }) => {
  return <button onClick={onClick}>{value}</button>
}

// Nécéssairement en dessous du composant

Button.propTypes = {
  value: PropTypes.string,
  onClick: PropTypes.func.isRequired,
}

Button.defaultProps = {
  value: "",
}

export default Button

Avec une fonction "normale".

import React from "react"
import PropTypes from "prop-types"

// Peut être déplacé au dessus du composant

Button.propTypes = {
  value: PropTypes.string,
  onClick: PropTypes.func.isRequired,
}

Button.defaultProps = {
  value: "",
}

function Button({ value, onClick }) {
  return <button onClick={onClick}>{value}</button>
}

export default Button

Les fonctions nous laissent le choix et nous permettent ainsi de réorganiser notre code en plaçant la signature du composant au début du fichier.

Conclusion

Il y a une chose à ne pas oublier lorsque l'on utilise React : "C'est juste du JS". Tous les concepts que l'on connait sont valides et tous ceux que l'on découvrira le seront.

Grace au hoisting on peut arranger notre code de façon à avoir directement accès à la signature de notre composant. Cela évite de devoir scroller en bas de fichier pour savoir quelles sont les props que l'on peut utiliser.

Une question demeure : faut-il utiliser cette astuce ?
Faites en fonction de vos préférences.
Personnellement je l'utilise mais ce n'est en rien une règle absolue.

Merci de m'avoir lu.

Liens

Maxime Blanc


© 2020 Maxime Blanc. Tous droits réservés.