Menu

Le hook useRef de React : refs DOM et valeurs persistantes

useRef donne à un composant une boîte, { current }, qui garde son contenu d'un rendu à l'autre sans provoquer de rendu quand vous la modifiez. Utilisez-la pour atteindre un nœud DOM (focus, défilement, mesure) ou pour stocker un identifiant de minuteur ou toute valeur que l'écran n'affiche pas.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

useRef renvoie un objet, { current: initialValue }, que React conserve pendant toute la vie du composant. Modifier ref.current ne provoque aucun nouveau rendu. Les refs conviennent donc à deux tâches : détenir un nœud DOM pour appeler des méthodes comme focus() dessus, et mémoriser d'un rendu à l'autre une valeur que l'écran n'a pas besoin d'afficher.

Passer l'objet ref à ref={inputRef} indique à React de placer le nœud DOM <input> dans inputRef.current. Remplacez focus() par select(), tapez quelque chose et cliquez à nouveau : le texte est surligné.

La syntaxe

const myRef = useRef(initialValue);

myRef.current;          // read
myRef.current = value;  // write, no re-render
  • initialValue n'est utilisée qu'au premier rendu. Pour les refs DOM, elle vaut null.
  • React renvoie le même objet à chaque rendu. Seul current change.
  • Lire et écrire current relève du JavaScript ordinaire. React ne le suit pas.

Une valeur qui ne provoque pas de rendu

L'état sert à ce que l'écran affiche. Une ref sert à ce dont le composant doit se souvenir sans l'afficher. Ce bloc garde un compteur dans une ref et un autre dans l'état, et affiche les deux dans la console :

Cliquez plusieurs fois sur Ref + 1. La console montre ref.current qui augmente, mais la page affiche toujours 0 et aucun rendu n'est signalé. Cliquez maintenant sur State + 1 : le composant refait un rendu, et le compteur de la ref à l'écran saute au nombre que la ref contenait déjà. La ref a gardé sa valeur tout du long ; React n'avait simplement aucune raison de la dessiner.

Refs ou état

useStateuseRef
Renvoie[value, setValue]{ current: value }
La modifierRefait le rendu du composantRien de visible
Quand la nouvelle valeur est lisibleAu rendu suivantImmédiatement
La modifier avecLe setterUne affectation, ref.current = x
Utile pourTout ce que l'écran afficheIdentifiants de minuteur, nœuds DOM, valeurs précédentes, indicateurs

En cas de doute, demandez-vous si le JSX lit la valeur. Si oui, elle a sa place dans l'état.

Stocker un identifiant de minuteur

setInterval renvoie un identifiant dont vous avez besoin plus tard pour arrêter le minuteur. C'est un bon usage pour une ref : le gestionnaire Stop en a besoin, mais l'écran ne l'affiche jamais, et le garder dans une simple variable le perdrait au rendu suivant.

Remplacez useRef(null) par let intervalRef = { current: null };, cliquez sur Start, attendez deux secondes, puis cliquez sur Stop : le minuteur continue. Chaque tic refait le rendu du composant, ce qui crée un nouvel objet, donc Stop n'arrête rien.

Mémoriser la valeur précédente

Un effet s'exécute après le rendu : il peut donc comparer la nouvelle valeur à celle stockée dans une ref, puis enregistrer la nouvelle pour la fois suivante :

Cliquez sur green, puis sur blue : la console affiche changed from red to green et changed from green to blue. Cliquer sur la couleur déjà sélectionnée n'affiche rien, car l'effet ne s'exécute que lorsque color change.

Travailler avec le DOM

Les refs permettent d'appeler les API du navigateur pour lesquelles React n'a pas de prop : focus(), select(), scrollIntoView(), play() sur une vidéo, ou lire une taille avec getBoundingClientRect().

const lastItemRef = useRef(null);

function scrollToLast() {
    lastItemRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}

<li ref={lastItemRef}>Last item</li>

ref.current vaut null pendant le premier rendu, car React n'a pas encore créé le nœud DOM, et à nouveau après le retrait de l'élément. Lisez-le dans les gestionnaires d'événements et les effets, là où le nœud existe. Pour mesurer un élément et positionner quelque chose avant que le navigateur affiche la page, lisez la ref dans useLayoutEffect.

Ne lisez ni n'écrivez ref.current pendant le rendu

React attend que le corps du composant soit un calcul pur : mêmes props et même état, même JSX. Une ref change en dehors de ce modèle, donc un rendu qui la lit ou l'écrit peut afficher des résultats différents pour les mêmes entrées, et React peut appeler le corps plus souvent que vous ne le pensez.

function Bad() {
    const count = useRef(0);
    count.current++;              // writing during render
    return <p>{count.current}</p>; // reading during render
}

Lisez et écrivez les refs dans les gestionnaires d'événements et les effets. La seule exception est l'initialisation paresseuse, où vous remplissez la ref une seule fois au premier rendu :

const playerRef = useRef(null);
if (playerRef.current === null) {
    playerRef.current = new VideoPlayer(); // runs once
}

(La démo du compteur dans une ref ci-dessus lit volontairement clicks.current dans le JSX, pour montrer que l'écran ne la suit pas. Ne reproduisez pas cela dans du vrai code.)

Ref callbacks

Au lieu d'un objet ref, vous pouvez passer une fonction à ref. React l'appelle avec le nœud DOM quand l'élément est ajouté. Dans React 19, la fonction peut renvoyer un nettoyage, que React appelle quand l'élément est retiré. C'est pratique pour une liste, où vous ne pouvez pas appeler useRef une fois par élément :

Au chargement, la console affiche une ligne attached par élément. Cliquez sur Remove last et seul detached Cheese apparaît. trackNode est défini hors du composant, c'est donc la même fonction à chaque rendu et React laisse les autres éléments tranquilles. Écrivez-le inline sous la forme ref={(node) => { ... }} et chaque clic détache puis rattache tous les éléments, car chaque rendu passe une nouvelle fonction.

Passer une ref à votre propre composant

Dans React 19, ref est une prop ordinaire pour les composants fonctions. Le parent crée la ref, et l'enfant la place sur l'élément DOM qu'il veut exposer :

Avant React 19, il fallait forwardRef, qui fonctionne toujours. La page sur forwardRef montre les deux versions et comment n'exposer que quelques méthodes avec useImperativeHandle.

Erreurs courantes

Afficher la valeur d'une ref dans le JSX. L'écran ne la suit pas. Si l'utilisateur doit voir la valeur, utilisez l'état.

Utiliser ref.current comme dépendance d'un effet. [ref.current] lit la valeur pendant le rendu et React ne sait pas quand elle change, donc l'effet ne se relance pas quand elle change. Utilisez une ref callback pour réagir au rattachement d'un nœud.

Lire une ref DOM pendant le premier rendu. Elle vaut null tant que React n'a pas créé le nœud. Lisez-la dans un effet ou un gestionnaire.

Créer un nouvel objet au lieu d'utiliser le hook. const ref = { current: null } dans le corps du composant est un nouvel objet à chaque rendu. Seul useRef vous donne le même à chaque fois.

Questions fréquentes

Que renvoie useRef ?

Un simple objet avec une seule propriété, current, initialisée à la valeur que vous passez. React renvoie le même objet à chaque rendu, donc ce que vous mettez dans current est toujours là la fois suivante.

Quelle est la différence entre useRef et useState ?

Modifier l'état refait le rendu du composant ; modifier ref.current, non. Utilisez l'état pour les valeurs que l'écran affiche, et une ref pour les valeurs dont seuls vos gestionnaires d'événements et vos effets ont besoin, comme un identifiant de minuteur ou un nœud DOM.

Pourquoi ref.current vaut-il null ?

React place le nœud DOM dans ref.current après l'avoir créé, pendant le commit. Pendant le premier rendu, il vaut encore null, et il revaut null après le retrait de l'élément. Lisez-le dans un gestionnaire d'événement ou un effet.

Modifier ref.current provoque-t-il un nouveau rendu ?

Non. React ne sait pas que vous l'avez modifié. La nouvelle valeur n'apparaît à l'écran que si autre chose, comme une mise à jour d'état, refait le rendu du composant.

Comment passer une ref à un composant enfant ?

Dans React 19, un composant fonction reçoit ref comme une prop ordinaire : vous passez <MyInput ref={inputRef} /> et l'enfant la place sur son <input>. Les versions plus anciennes nécessitaient forwardRef.

Illustration des langages de programmation de Coddy

Apprendre à coder avec Coddy

COMMENCER