useRef est un Hook React qui vous permet de référencer une valeur qui n’est pas nécessaire au code du rendu lui-même.

const ref = useRef(initialValue)

Référence

useRef(initialValue)

Appelez useRef au niveau racine de votre composant pour déclarer une ref.

import { useRef } from 'react';

function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
// ...

Voir d’autres exemples ci-dessous.

Paramètres

  • initialValue : la valeur initiale pour la propriété current de l’objet ref. Elle peut être de n’importe quel type. Cet argument est ignoré après le rendu initial.

Valeur renvoyée

useRef renvoie un objet doté d’une unique propriété :

  • current : elle vaut initialement la initialValue que vous avez passée. Vous pourrez ensuite la modifier. Si vous passez l’objet ref à React en tant que prop ref d’un nœud JSX, React définira automatiquement sa propriété current.

Lors des rendus ultérieurs, useRef renverra le même objet.

Limitations

  • Vous pouvez modifier la propriété ref.current. Contrairement à l’état, elle est modifiable. En revanche, si vous y stockez un objet nécessaire au rendu (par exemple un morceau de votre état), vous ne devriez pas modifier cet objet.
  • Lorsque vous modifiez la propriété ref.current, React ne refait pas de rendu de votre composant. React n’est pas au courant de vos modifications parce qu’une ref est un objet JavaScript brut.
  • Évitez d’écrire ou même de lire ref.current lors du rendu, sauf pour l’initialiser. Ça rendrait le comportement de votre composant imprévisible.
  • En Mode Strict, React appellera votre fonction composant deux fois afin de vous aider à repérer des impuretés accidentelles. Ce comportement est limité au développement et n’affecte pas la production. Chaque objet ref sera créé deux fois, mais une de ses versions sera ignorée. Si votre fonction composant est pure (ce qui devrait être le cas), ça n’affectera en rien son comportement.

Utilisation

Référencer une valeur avec une ref

Appelez useRef au niveau racine de votre composant pour déclarer une ou plusieurs refs.

import { useRef } from 'react';

function Stopwatch() {
const intervalRef = useRef(0);
// ...

useRef renvoie un objet ref avec une unique propriété current initialement définie à la valeur initiale que vous avez fournie.

Lors des rendus ultérieurs, useRef renverra le même objet. Vous pouvez en modifier la propriété current pour stocker une information que vous relirez plus tard. Ça vous rappelle peut-être l’état, mais il y a une différence fondamentale.

Modifier une ref ne redéclenche pas le rendu. Ça signifie que les refs sont idéales pour stocker des informations qui n’affectent pas le résultat visuel de votre composant. Par exemple, si vous devez stocker un identifiant de timer et le récupérer plus tard, mettez-le dans une ref. Pour mettre à jour la valeur d’une ref, vous devez modifier manuellement sa propriété current :

function handleStartClick() {
const intervalId = setInterval(() => {
// ...
}, 1000);
intervalRef.current = intervalId;
}

Par la suite, vous pouvez lire l’identifiant du timer depuis la ref afin de décommissionner le timer :

function handleStopClick() {
const intervalId = intervalRef.current;
clearInterval(intervalId);
}

En utilisant une ref, vous garantissez que :

  • Vous pouvez stocker de l’information d’un rendu à l’autre (contrairement aux variables classiques, réinitialisées à chaque rendu).
  • La modifier ne déclenche pas un nouveau rendu (contrairement aux variables d’état, qui déclenchent un nouveau rendu).
  • L’information reste locale à chaque instance de votre composant (contrairement aux variables extérieures, qui sont partagées).

Modifier une ref ne déclenche pas de nouveau rendu, de sorte que les refs sont inadaptées au stockage d’information que vous souhaitez afficher à l’écran. Utilisez plutôt des variables d’état pour ça. Apprenez comment choisir entre useRef et useState.

Exemples de référencements de valeurs avec useRef

Exemple 1 sur 2 ·
Compteur de clics

Ce composant utilise une ref pour garder le compte du nombre de fois qu’un bouton a été cliqué. Notez qu’on peut parfaitement utiliser une ref plutôt qu’une variable d’état dans ce cas, puisque le compteur n’est lu et écrit qu’au sein d’un gestionnaire d’événement.

import { useRef } from 'react';

export default function Counter() {
  let ref = useRef(0);

  function handleClick() {
    ref.current = ref.current + 1;
    alert('Vous avez cliqué ' + ref.current + ' fois !');
  }

  return (
    <button onClick={handleClick}>
      Cliquez ici !
    </button>
  );
}

Si vous affichiez {ref.current} dans le JSX, le nombre ne serait pas mis à jour au clic. C’est parce que la redéfinition de ref.current ne déclenche pas de nouveau rendu. Les informations utilisées par le rendu devraient plutôt être stockées dans des variables d’état.

Piège

N’écrivez pas et ne lisez pas ref.current lors du rendu.

React s’attend à ce que le corps de votre composant se comporte comme une fonction pure :

  • Si les entrées (les props, l’état et le contexte) sont les mêmes, votre composant devrait renvoyer exactement le même JSX.
  • L’appeler dans un ordre différent ou avec des arguments différents ne devrait pas affecter les résultats des autres appels.

Lire ou écrire une ref lors du rendu va à l’encontre de ces attentes.

function MyComponent() {
// ...
// 🚩 N'écrivez pas une ref pendant le rendu
myRef.current = 123;
// ...
// 🚩 Ne lisez pas une ref pendant le rendu
return <h1>{myOtherRef.current}</h1>;
}

Vous pouvez plutôt lire ou écrire des refs au sein de gestionnaires d’événements ou d’Effets.

function MyComponent() {
// ...
useEffect(() => {
// ✅ Vous pouvez lire ou écrire des refs dans un Effet
myRef.current = 123;
});
// ...
function handleClick() {
// ✅ Vous pouvez lire ou écrire des refs dans un gestionnaire d'événement
doSomething(myOtherRef.current);
}
// ...
}

Si vous devez lire ou écrire quelque chose durant le rendu, utilisez plutôt un état.

Lorsque vous enfreignez ces règles, votre composant restera peut-être opérationnel, mais la plupart des nouvelles fonctionnalités que nous sommes en train d’ajouter à React dépendent de ces attentes. Apprenez plutôt comment garder vos composants purs.


Manipuler le DOM avec une ref

Il est particulièrement courant d’utiliser une ref pour manipuler le DOM. React prend nativement en charge ce cas de figure.

Commencez par déclarer un objet ref avec une valeur initiale à null :

import { useRef } from 'react';

function MyComponent() {
const inputRef = useRef(null);
// ...

Passez ensuite votre objet ref comme prop ref du JSX d’un nœud DOM que vous souhaitez manipuler :

// ...
return <input ref={inputRef} />;

Une fois que React a créé le nœud DOM et l’a mis à l’écran, React définira la propriété current de votre objet ref pour pointer sur ce nœud DOM. Vous pouvez désormais accéder au nœud DOM de l’<input> et appeler des méthodes telles que focus() :

function handleClick() {
inputRef.current.focus();
}

React remettra la propriété current à null lorsque le nœud sera retiré de l’écran.

Découvrez plus en détails la manipulation du DOM avec des refs.

Exemples de manipulation du DOM avec useRef

Exemple 1 sur 4 ·
Activer un champ

Dans cet exemple, cliquer sur le bouton activera le champ de saisie :

import { useRef } from 'react';

export default function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>
        Activer le champ de saisie
      </button>
    </>
  );
}


Éviter de recréer le contenu de la ref

React sauvegardera la valeur initiale de la ref au premier rendu, puis l’ignorera lors des rendus ultérieurs.

function Video() {
const playerRef = useRef(new VideoPlayer());
// ...

Même si le résultat de new VideoPlayer() n’est utilisé que lors du rendu initial, vous appelez quand même cette fonction à chaque rendu. Ça peut gâcher les performances si cette création d’objet est coûteuse.

Pour résoudre ça, utilisez plutôt une initialisation conditionnelle, comme ceci :

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

En temps normal, lire ou écrire ref.current lors du rendu est interdit. Ceci dit, c’est acceptable dans ce cas précis car le résultat sera toujours le même, et que la condition n’est vérifiée que lors de l’initialisation, ce qui la rend pleinement prévisible.

En détail

Comment éviter des vérifications de null lors d’appels ultérieurs à useRef

Si vous utilisez un vérificateur de types et souhaitez ne pas toujours avoir à vérifier le cas null, vous pouvez plutôt tenter une approche comme celle-ci :

function Video() {
const playerRef = useRef(null);

function getPlayer() {
if (playerRef.current !== null) {
return playerRef.current;
}
const player = new VideoPlayer();
playerRef.current = player;
return player;
}

// ...

Ici, la playerRef elle-même peut être null. En revanche, vous devriez arriver à convaincre votre vérificateur de types qu’il n’y a aucun scénario dans lequel getPlayer() renvoie null. Utilisez alors getPlayer() dans vos gestionnaires d’événements.


Dépannage

Je n’arrive pas à obtenir une ref sur un composant personnalisé

Si vous tentez de passer une ref à votre propre composant, comme ceci :

const inputRef = useRef(null);

return <MyInput ref={inputRef} />;

…vous obtiendrez peut-être une erreur dans la console :

Console
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

(«  Avertissement : les fonctions composants ne peuvent pas recevoir de refs. Toute tentative d’accéder à cette ref échouera. Souhaitiez-vous utiliser React.forwardRef() ? », NdT)

Par défaut, vos propres composants n’exposent pas de refs vers les nœuds DOM qu’ils contiennent.

Pour corriger ça, trouvez le composant vers lequel vous souhaitez obtenir une ref :

export default function MyInput({ value, onChange }) {
return (
<input
value={value}
onChange={onChange}
/>
);
}

Enrobez-le alors dans un forwardRef comme ceci :

import { forwardRef } from 'react';

const MyInput = forwardRef(({ value, onChange }, ref) => {
return (
<input
value={value}
onChange={onChange}
ref={ref}
/>
);
});

export default MyInput;

Le composant parent peut désormais obtenir une ref vers lui.

Explorez plus avant comment accéder au nœuds DOM d’un autre composant.