Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 4

Utilisation de `useRef` pour des références persistantes

Recommended resources

Bonjour ! Dans la leçon précédente, nous avons vu comment utiliser useEffect pour exécuter du code au moment où un composant apparaît à l'écran. Nous avons même eu un aperçu du hook useRef dans l'exercice pour mettre le focus sur un champ de saisie.

Introduction

Aujourd'hui, nous allons nous plonger entièrement dans useRef. Si useState est la "mémoire" d'un composant qui affecte ce qui est affiché, et useEffect est le moyen de se synchroniser avec l'extérieur, alors useRef est la "poche secrète" du composant : un endroit pour garder des informations qui ne doivent pas affecter l'affichage.

L'objectif de cette leçon est d'utiliser le hook useRef pour stocker des références persistantes sans provoquer de re-rendu.

Cette compétence est absolument fondamentale pour notre objectif de créer des scènes 3D :

  1. C'est avec useRef que nous obtiendrons une référence directe à l'élément <canvas> HTML, qui sera la "toile" sur laquelle Three.js dessinera notre univers 3D.
  2. Plus tard, avec React Three Fiber, useRef nous permettra de "saisir" directement un objet 3D (un cube, une lumière, une caméra) pour le manipuler en code, par exemple pour créer une animation.

À la fin de cette leçon, vous saurez comment utiliser useRef pour ses deux fonctions principales : accéder à des éléments du DOM et conserver des données entre les rendus.


1. Qu'est-ce que useRef ?

Le hook useRef est étonnamment simple. Quand vous l'appelez, il crée un objet JavaScript standard qui ne contient qu'une seule propriété : current.

import { useRef } from 'react';

// Dans votre composant
const maRef = useRef(0); // La valeur initiale est 0

console.log(maRef); // Affiche : { current: 0 }

La magie de useRef réside dans deux points clés :

  • Persistance : L'objet maRef retourné par useRef est le même à chaque rendu. React s'assure de vous redonner exactement le même objet tout au long de la vie du composant.
  • Pas de re-rendu : Si vous modifiez la valeur de maRef.current (par ex. maRef.current = 5), React ne déclenchera PAS un nouveau rendu.

C'est la différence fondamentale avec useState. Changer un état avec setMonEtat() provoque un re-rendu pour mettre l'interface à jour. Modifier maRef.current est une opération silencieuse.


2. Premier cas d'usage : Accéder aux éléments du DOM

C'est l'utilisation la plus courante et la plus intuitive de useRef. Comme nous l'avons fait dans l'exercice précédent, on peut l'utiliser pour obtenir une référence directe à un élément HTML (un input, une div, un canvas...).

Le processus se fait en deux étapes :

  1. Créez une ref avec useRef(null). La valeur initiale est null car l'élément DOM n'existe pas encore.
  2. Attachez cette ref à un élément JSX via la prop spéciale ref : <input ref={inputRef} />.

Après le rendu du composant, React placera l'élément DOM de l'input dans la propriété inputRef.current.

La vidéo suivante explique très clairement ce mécanisme.

Apprendre React : Le hook useRef

Cette vidéo de Grafikart.fr explique la base de useRef et montre concrètement comment l'utiliser pour récupérer une référence vers un élément HTML. C'est le point de départ idéal.

Regardez les deux premières parties : De 00:00 à 01:35 : Introduction au hook, sa structure { current: ... } et le fait que sa modification ne provoque pas de re-rendu. De 01:35 à 03:55 : Comment attacher la ref à un élément HTML et pourquoi il faut utiliser useEffect pour accéder à ref.current en toute sécurité.

Comme l'explique la vidéo, il est crucial d'accéder à la propriété .current de la ref à l'intérieur d'un useEffect. Pourquoi ? Car useEffect avec un tableau de dépendances vide [] s'exécute après que le composant a été monté et que le DOM a été créé. Si vous essayez d'accéder à ref.current directement dans le corps du composant pendant le premier rendu, il sera encore null.

React useRef Hook Example
Ce code illustre parfaitement le schéma : on crée une ref, on l'attache à l'input, puis on utilise `useEffect` pour appeler `inputRef.current.focus()` une fois le composant monté.

3. Second cas d'usage : Conserver des valeurs sans re-rendu

L'autre grande utilité de useRef est de stocker n'importe quelle valeur dont vous avez besoin de garder la trace entre les rendus, mais dont le changement ne doit pas impacter l'interface graphique.

Un exemple classique est l'ID renvoyé par un setTimeout ou setInterval. Vous avez besoin de cet ID pour pouvoir appeler clearTimeout plus tard, mais l'afficher à l'utilisateur n'a aucun intérêt. Si vous le stockiez dans un état, chaque setInterval provoquerait un re-rendu inutile.

La documentation officielle de React propose un excellent exemple avec un chronomètre.

Référencer des valeurs avec les refs - React

La documentation officielle de React est une ressource incontournable. Lisez cet article pour bien comprendre la différence entre useRef et useState, et pour voir un exemple concret de leur utilisation conjointe.

Concentrez-vous sur deux sections : "Exemple : construire un chronomètre" : Observez comment useState est utilisé pour les valeurs affichées (le temps) et useRef pour une valeur technique (l'ID de l'intervalle). "Différences entre refs et variables d’état" : Le tableau comparatif est particulièrement utile pour résumer les différences.

useRef vs useState vs une simple variable

Pour bien ancrer la différence, voici un résumé :

useState useRef let variable
Re-rendu si modifié ? Oui Non Non
Valeur persistante ? Oui Oui Non (réinitialisée à chaque rendu)
Quand l'utiliser ? Pour toute donnée qui doit être affichée dans le JSX. Pour des données techniques (ID de timer, référence DOM) ou des valeurs qui doivent persister sans affecter l'UI. Pour des calculs temporaires à l'intérieur d'un rendu ou d'une fonction.

Le site Codelynx propose une démonstration interactive qui vous permettra de "sentir" cette différence.

Comment utilisé useRef en React ? Guide complet - Codelynx

Cet article de Codelynx contient une excellente démonstration pratique pour visualiser l'impact de useState, useRef et une variable simple sur les re-rendus.

Allez directement à la section "useRef vs useState vs variable". Lisez le code d'exemple, puis jouez avec la démo interactive juste en dessous. Cliquez plusieurs fois sur les boutons "Increment ref" et "Increment variable", puis sur "Increment state" et observez ce qui se passe.


4. Exercice : Corriger un timer

Mettons cela en pratique. L'exercice suivant, tiré de la documentation de React, présente un bug classique. Un setTimeout est créé, mais le bouton "Annuler" ne fonctionne pas. Votre mission est de trouver pourquoi et de le corriger en utilisant le bon outil.

Relevez ces défis - React

C'est l'heure de coder ! Cet exercice va tester votre compréhension. Vous devez corriger un composant où clearTimeout ne fonctionne pas comme prévu.

Lisez la description du "Défi 1 sur 4 : Corriger un bouton de discussion". Analysez le code fourni. La variable timeoutID est déclarée avec let. Pourquoi est-ce un problème ? Comment useRef peut-il résoudre ce problème ? Essayez de le corriger par vous-même avant de chercher la solution.

Indice Chaque fois que le composant est re-rendu (par exemple, quand `isSending` passe à `true`), la fonction `Chat` est ré-exécutée du début à la fin. Qu'arrive-t-il à une variable déclarée avec `let` à chaque exécution ?
Solution

Le problème vient du fait que let timeoutID est réinitialisée à null à chaque rendu. Lorsque vous cliquez sur "Envoyer", l'état isSending change, ce qui provoque un re-rendu. Dans la fonction handleSend, l'ID du timer est bien assigné à timeoutID. Mais lorsque vous cliquez sur "Annuler", un autre re-rendu se produit, et au moment où handleUndo est appelé, timeoutID a déjà été réinitialisée à null par la nouvelle exécution de la fonction Chat. clearTimeout(null) ne fait rien.

La solution est de stocker l'ID dans une ref, qui persistera entre les rendus.

import { useState, useRef } from 'react';

export default function Chat() {
  const [text, setText] = useState('');
  const [isSending, setIsSending] = useState(false);
  
  // On remplace 'let' par 'useRef'
  const timeoutRef = useRef(null);

  function handleSend() {
    setIsSending(true);
    // On stocke l'ID dans la propriété .current de la ref
    timeoutRef.current = setTimeout(() => {
      alert('Envoyé !');
      setIsSending(false);
    }, 3000);
  }

  function handleUndo() {
    setIsSending(false);
    // On lit l'ID depuis la propriété .current de la ref
    clearTimeout(timeoutRef.current);
  }

  return (
    <>
      <input
        disabled={isSending}
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <button
        disabled={isSending}
        onClick={handleSend}>
        {isSending ? 'Envoi...' : 'Envoyer'}
      </button>
      {isSending && (
        <button onClick={handleUndo}>
          Annuler
        </button>
      )}
    </>
  );
}

Conclusion

Excellent travail ! Vous avez maintenant ajouté un outil puissant et polyvalent à votre boîte à outils React. useRef peut sembler moins direct que useState au premier abord, mais il est essentiel pour interagir avec le monde extérieur à React (comme le DOM) et pour optimiser les performances en évitant des re-rendus inutiles.

Points clés à retenir :

  • useRef(initialValue) renvoie un objet persistant { current: initialValue }.
  • Modifier la propriété .current d'une ref ne déclenche pas de re-rendu.
  • Le premier cas d'usage est d'accéder à un élément du DOM en passant la ref à la prop ref d'un élément JSX.
  • Le second cas d'usage est de stocker toute valeur mutable qui doit survivre aux re-rendus sans les déclencher (ex: ID de timers, instances de classes...).

Aperçu de la prochaine leçon :
Nous avons bien exploré comment gérer l'état et les références à l'intérieur d'un seul composant. Mais une application React est un arbre de composants. La prochaine étape logique est d'apprendre à faire communiquer ces composants entre eux. Nous verrons comment passer des données d'un composant parent à un composant enfant en utilisant les props. C'est le mécanisme de base qui rend les applications React modulaires et réutilisables.