Comprendre et utiliser le Hook useRef en React
Le hook useRef permet de créer une référence mutable qui persiste tout au long du cycle de vie du composant, sans déclencher de nouveau rendu lorsque sa valeur change.
Définition du hook useRef
- Il prend en paramètre une valeur initiale qui est affectée à la propriété
current. - Il retourne un objet contenant une unique propriété
current.
const ref = useRef(null);
Caractéristiques du hook useRef
- Modifier la valeur de
ref.currentn’entraîne pas un nouveau rendu du composant. - Le
useRefest spécifique à chaque composant, donc si un composant est utilisé plusieurs fois, chaque instance aura sa propre référence distincte.
Principaux cas d'utilisation du hook useRef
1. Références DOM
L'une des utilisations principales de useRef est de créer des références aux éléments DOM, permettant ainsi d'accéder et d'interagir avec des éléments HTML directement.
import { useRef, useEffect } from "react";
export default function MyApp() {
const ref = useRef(null);
useEffect(() => {
ref.current.focus();
}, []);
return <input type="text" ref={ref} />;
}
Lorsque l'élément HTML est supprimé, la valeur de ref.current est automatiquement réinitialisée à null.
2. Stockage de données persistantes entre les rendus
Le hook useRef est également utile pour stocker des informations qui doivent persister entre les rendus mais ne doivent pas déclencher de ré-rendu lorsqu'elles sont mises à jour. Cela inclut des variables comme un timer ou des informations de la précédente interaction utilisateur.
const previousValue = useRef(null);
useEffect(() => {
previousValue.current = value; // Mémorise la valeur précédente sans provoquer de rendu
}, [value]);
Comment obtenir une référence à un composant enfant avec useRef ?
Vous pourriez instinctivement penser à passer une prop nommée ref à un composant enfant pour lui assigner une référence à un élément DOM, comme dans l'exemple suivant :
import { useRef, useState, useEffect } from "react";
function MyInput({ value, onChange, ref }) {
return (
<input value={value} ref={ref} onChange={(e) => onChange(e.target.value)} />
);
}
export default function MyApp() {
const ref = useRef(null);
const [title, setTitle] = useState("");
useEffect(() => {
console.log(ref);
}, []);
return <MyInput value={title} ref={ref} onChange={setTitle} />;
}
Cependant, cette approche vous mènera à l'erreur suivante :
MyInput: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop.
En effet, dans React, la propriété ref n'est pas une prop classique. Elle ne peut pas être directement transmise comme une prop standard aux composants enfants.
Solution 1 : Utiliser un nom de prop différent pour la référence
Une solution consiste à renommer la propriété utilisée pour la référence, par exemple en la nommant inputRef au lieu de ref. Ainsi, vous évitez d'entrer en conflit avec le comportement réservé de ref.
import { useRef, useState, useEffect } from "react";
function MyInput({ value, onChange, inputRef }) {
return (
<input
value={value}
ref={inputRef}
onChange={(e) => onChange(e.target.value)}
/>
);
}
export default function MyApp() {
const ref = useRef(null);
const [title, setTitle] = useState("");
useEffect(() => {
console.log(ref);
}, []);
return <MyInput value={title} inputRef={ref} onChange={setTitle} />;
}
Solution 2 : Utiliser forwardRef pour transmettre la référence
Une autre solution plus appropriée consiste à utiliser la fonction forwardRef de React, qui permet explicitement de transférer une référence depuis un composant parent vers un composant enfant.
import { useRef, useState, useEffect, forwardRef } from "react";
const MyInput = forwardRef(function ({ value, onChange }, ref) {
return (
<input value={value} ref={ref} onChange={(e) => onChange(e.target.value)} />
);
});
export default function MyApp() {
const ref = useRef(null);
const [title, setTitle] = useState("");
useEffect(() => {
console.log(ref);
}, []);
return <MyInput value={title} ref={ref} onChange={setTitle} />;
}
Dans React 19, les refs sont désormais transmises directement comme des props, éliminant ainsi le besoin d’utiliser forwardRef.
Personnaliser l’interface d’une ref avec useImperativeHandle
Dans l’exemple précédent, le composant parent obtient une référence directe sur le champ de saisie <input> grâce à forwardRef. Cependant, dans certains cas, on ne souhaite pas exposer toute la ref interne du composant enfant, mais uniquement certaines méthodes spécifiques.
C’est précisément le rôle du hook useImperativeHandle : il permet de choisir quelles méthodes ou propriétés un composant enfant rend accessibles à son parent via une ref.
Exemple : exposer une méthode focus() depuis un composant enfant
import { useRef, useState, useEffect, forwardRef, useImperativeHandle } from "react";
const MyInput = forwardRef(function ({ value, onChange }, ref) {
const inputRef = useRef(null);
// Définir ce que la ref du parent pourra utiliser
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
}));
return (
<input
value={value}
ref={inputRef}
onChange={(e) => onChange(e.target.value)}
placeholder="Tapez quelque chose..."
/>
);
});
export default function MyApp() {
const ref = useRef(null);
const [title, setTitle] = useState("");
useEffect(() => {
console.log(ref); // ref.current contient { focus }
}, []);
return (
<div>
<MyInput value={title} ref={ref} onChange={setTitle} />
<button onClick={() => ref.current.focus()}>Focus input</button>
</div>
);
}
-
Le composant
MyInpututilise une ref interneinputRefpour accéder au champ<input>. -
Avec useImperativeHandle, il expose au parent seulement la méthode
focus(). - Le parent
MyApppeut maintenant appelerref.current.focus()sans connaître la structure interne de MyInput.
Ainsi, la logique interne du composant enfant expose une API publique claire et maîtrisée.