useRef est un hook React qui crée un objet ref mutable avec une propriété .current, conservé entre les rendus du composant. Contrairement à useState, modifier .current ne provoque pas de re-rendu, ce qui rend useRef idéal pour stocker des références DOM, des minuteurs et toutes les valeurs qui doivent persister entre les rendus sans redessiner l’interface. Selon la Documentation React (2025), l’objet ref est créé une fois pendant la durée de vie du composant et ne change pas entre les rendus, garantissant ainsi la stabilité de la référence.
Points clés
useRef est un hook ajouté dans React 16.8 qui retourne un objet ref mutable avec une seule propriété .current. La valeur initiale est attribuée à .current lors du montage du composant. La principale différence avec une variable ordinaire est que l’objet ref survit aux re-rendus : lors des rendus suivants, React ne recrée pas le ref mais retourne le même objet.
En interne, useRef utilise le même mécanisme que useState et useEffect — une liste chaînée de hooks sur le nœud fiber. Cependant, contrairement à useState, React ne suit pas les modifications de l’objet ref, ne met pas en file d’attente les mises à jour et ne planifie pas de re-rendu. Cela fait de useRef un hook extrêmement léger sans impact sur les performances, même en cas de modifications fréquentes.
Selon l’équipe React — Hooks FAQ (2025), useRef est essentiellement une « boîte » qui stocke une valeur. Sucre syntaxique : const ref = useRef(valeurInitiale) équivaut à const ref = { current: valeurInitiale } à la différence que React garantit que l’objet ref sera le même à chaque rendu. Pas de magie — juste une référence stable gérée par React.
import { useRef } from 'react';
function Component() {
const countRef = useRef(0);
// countRef.current = 0 initialement
// countRef.current = 5 après mutation
// Aucun re-rendu ne se produit !
}
Le cas d’utilisation le plus courant de useRef est l’accès direct à un élément DOM. React attribue à elementRef.current une référence au nœud DOM après le montage du composant. Cela est nécessaire pour focuser une saisie, mesurer les dimensions d’un élément, intégrer des bibliothèques d’animation et des bibliothèques qui n’utilisent pas le rendu React.
React gère automatiquement les références ref sur le DOM : lors du montage, il attribue le nœud DOM réel ; lors du démontage, il le remet à null. Cela garantit que .current contient toujours la valeur à jour et empêche les fuites mémoire via des références pendantes à des éléments supprimés. Aucun nettoyage manuel n’est requis.
function AutoFocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// Focuser la saisie après le montage du composant
inputRef.current?.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
Selon la documentation React — Refs et DOM (2025), n’utilisez pas ref pour des opérations déclaratives — ouvrir/fermer des modales, gérer la visibilité. Pour ces fins, il existe state et props. Les refs sont destinées aux opérations impératives qui ne peuvent pas être exprimées de manière déclarative : focus, sélection de texte, intégration avec des bibliothèques tierces.
Le deuxième cas d’utilisation le plus important est le stockage de valeurs arbitraires qui doivent persister entre les rendus mais dont les modifications ne doivent pas déclencher de re-rendu. Exemples typiques : identifiants de minuteurs (setInterval/setTimeout), drapeaux d’annulation de requêtes, valeurs précédentes de props pour comparaison et toutes données n’affectant pas la sortie de l’interface.
C’est particulièrement utile dans useEffect : useRef stocke l’identifiant du minuteur et le nettoyage le supprime. Si timerId était stocké dans useState, chaque appel à setTimerId provoquerait un re-rendu inutile qui n’est nécessaire ni pour la logique ni pour l’interface. useRef résout ce problème sans surcharge et sans re-rendus inutiles.
function Timer() {
const intervalRef = useRef(null);
const start = () => {
intervalRef.current = setInterval(() => {
console.log('tic');
}, 1000);
};
const stop = () => {
clearInterval(intervalRef.current);
};
useEffect(() => stop, []);
return <><button onClick={start}>Start</button><button onClick={stop}>Stop</button></>;
}
Le dilemme principal — choisir entre useRef et useState — se résout par une question : « Un re-rendu est-il nécessaire lors du changement de valeur ? ». Si oui — useState. Si non — useRef. useState stocke l’état qui affecte la sortie du composant ; useRef stocke les données nécessaires à la logique interne mais n’affectant pas l’interface.
En pratique, les développeurs utilisent souvent useRef pour stocker des callbacks afin d’éviter les problèmes de fermeture. Par exemple, si useEffect s’abonne à un événement et que le callback nécessite l’état actuel — stockez le callback dans useRef. À chaque rendu, mettez à jour ref.current avec une nouvelle fonction, et l’effet invoquera toujours le callback frais sans besoin de se réabonner.
| Critère | useRef | useState |
|---|---|---|
| Re-rendu | Ne déclenche pas au changement | Déclenche à chaque setState |
| Mutation | Directe : ref.current = valeur | Via setter : setState(valeur) |
| Utilisation dans JSX | Non utilisé (aucun effet) | Utilisé dans la sortie du composant |
| Exemple | Minuteurs, références DOM, valeurs précédentes | Données de formulaire, état UI, drapeaux |
Il existe un anti-patron : utiliser useRef pour des données nécessaires dans JSX mais dont les modifications ne doivent pas provoquer de re-rendu. Cela entraîne une désynchronisation — l’interface affiche des données anciennes alors que ref.current est déjà nouveau. Si la valeur est affichée dans l’interface — utilisez useState. Si elle est utilisée uniquement en interne — useRef.
La combinaison useRef + useEffect est le modèle standard pour suivre les valeurs précédentes des props. Stockez la valeur précédente dans ref, comparez-la avec la valeur actuelle dans useEffect et prenez des décisions basées sur la différence. C’est particulièrement utile dans les animations quand vous avez besoin de connaître la valeur avant le changement.
function PriceDisplay({ price }) {
const prevPriceRef = useRef(price);
useEffect(() => {
const prevPrice = prevPriceRef.current;
if (price > prevPrice) {
animateUp();
} else if (price < prevPrice) {
animateDown();
}
prevPriceRef.current = price;
}, [price]);
return <span>${price}</span>;
}
Selon la documentation React — Hooks FAQ (2025), ce modèle est appelé « modèle de valeur précédente ». Il fonctionne car useRef conserve la valeur entre les rendus et useEffect s’exécute après la confirmation des modifications. D’abord le DOM est mis à jour avec le nouveau price, ensuite useEffect le compare avec le précédent (encore dans ref) et met à jour ref.current à la valeur actuelle.
L’erreur la plus fréquente est la lecture de ref.current pendant la phase de rendu pour calculer du JSX. Comme modifier ref.current ne déclenche pas de re-rendu, le composant peut utiliser une valeur obsolète. Si .current participe à la sortie de l’interface — utilisez useState. Si vous devez synchroniser ref et state, utilisez useEffect pour mettre à jour state à partir de ref.
// ❌ Ne lisez pas ref.current pendant le rendu pour l’affichage
function BadComponent() {
const valRef = useRef(0);
return <p>{valRef.current}</p>; // NE sera PAS mis à jour lors de la mutation
}
// ✅ Utilisez state pour l’affichage, ref pour la logique
function GoodComponent() {
const [val, setVal] = useState(0);
const valRef = useRef(0);
return <p>{val}</p>;
}
Une autre erreur courante est d’utiliser useRef comme seul moyen de stocker l’état dans un composant alors que les données affectent réellement l’interface. Les développeurs choisissent useRef pour éviter les re-rendus « inutiles », mais se retrouvent avec une interface qui ne se met pas à jour. L’approche correcte : utilisez useState pour les données d’interface et useRef uniquement pour les valeurs auxiliaires ne participant pas au rendu.
Questions fréquentes
Oui, c’est un modèle courant — le modèle d’état précédent. Créez un ref et mettez-le à jour dans useEffect à chaque changement de la valeur suivie. Entre le rendu et l’effet, ref.current contient la valeur précédente, qui peut être comparée à la valeur actuelle. Cela ne nécessite pas de re-rendus supplémentaires et fonctionne avec tout type de données.
React attribue à ref.current la valeur du nœud DOM seulement après que l’élément est rendu et ajouté au DOM réel. Pendant le premier rendu, le composant n’est pas encore monté, donc ref.current est égal à la valeur initiale (null). L’accès au DOM via ref.current n’est possible que dans useEffect ou dans les gestionnaires d’événements appelés après le montage.
createRef crée un nouvel objet ref à chaque rendu — il ne doit être utilisé que dans les composants de classe. useRef crée le ref une fois et retourne le même objet à tous les rendus suivants. Dans les composants fonctionnels, utilisez exclusivement useRef ; createRef entraînera une perte de valeur lors du re-rendu car l’objet sera recréé.
Oui, un objet ref peut être passé comme une prop normale : <Child inputRef={inputRef} />. Le composant enfant l’utilise via inputRef.current. Cependant, pour passer un ref directement à l’élément DOM d’un composant enfant, utilisez forwardRef — un HOC qui permet de transmettre des refs via les props. Sans forwardRef, le ref ne sera pas automatiquement lié à l’élément DOM à l’intérieur du composant enfant.
Attachez un ref à l’élément, puis dans useEffect lisez ref.current.getBoundingClientRect() ou ref.current.offsetWidth / offsetHeight. Pour le suivi réactif des changements de taille, utilisez ResizeObserver dans useEffect : créez un observer, abonnez-vous aux changements et mettez à jour state avec les nouvelles dimensions. N’oubliez pas de déconnecter l’observer dans le nettoyage.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi