Retour • 30/09/2024
Comment utilisé useRef en React ? Guide complet
Écris par Melvyn Malherbe le 30/09/2024
useRef
est un des hooks fondamental de React. Il permet de nombreuses choses
et il est, à mon sens, pas assez utilisé. Dans cet article on va essayer de
comprendre quelles sont les utilités du useRef
et ses différences avec
useState
.
Documentation de useRef
D'après la documentation React le useRef permet de stocker une valeur qui n'est pas nécessaire pour le rendu.
const Component = () => {
const ref = useRef(12);
console.log(ref.current); // 12
};
useRef
va retourner une ref object
avec une unique propriété nommée current
qui
contient la valeur stockée. Dans le prochain render la ref sera toujours la même...
il faut la modifier et utiliser la méthode current.current = value
On peut notamment l'utiliser pour récupérer la référence d'un élément du DOM.
Ref pour récupérer un élément du DOM
Je te propsoe de faire un petit détour pour voir l'utilisation de useRef
avec la props ref
pour récupérer un élément du DOM
.
Chaque élément par défaut (div
, span
, etc...) possède une propriété ref
qui permet de récupérer la référence de l'élément.
Cette proipriété ref
peut prendre 2 type de paramptre :
- Un callback (une méthode) qui prends la référence en paramètre
- Un object et dans ce cas, par défaut il définit la propriété
current
comme la ref
Quand je dis ref
dans le contexte d'un élément React, je parle de l'élément du
DOM associé. React gère la majorité des choses pour toi, mais parfois on souhaites
réucpérer l'élément du DOM pour pouvoir le manipuler.
Voici un exemple, tu peux voir que dans les deux cas il est possible de récupérer la référence de l'élément :
Quand on utilise useRef
, on utilise la deuxième méthode, du moment que la props ref
prends un object en paramètre, elle définit la valeur de la propriété current
comme la ref.
Ensuite, useRef
va stocker la valeur entre les render spécifiquement pour le composnats, on verra ensuite comment.
Maintenat parlons sérieux...
C'est quoi une ref ?
Peux-tu deviner la valeur de value.current
ici :
const value = { current: 0 };
const value2 = value;
const value3 = value2;
value3.current = 1;
Réponse 1
Visuellement ce code donne ça :
value
{"current":0}
value2
{"current":0}
value3
{"current":0}
{"current":0}
En cliquant sur Next tu crées une nouvelle variable qui reprend la valeur
précédente. En cliquant sur "Set current value" tu incrémentes de 1
current
.
Tu vois que toutes les variables changent. Tu as en fait tu as changé la valeur de leur référence (carré orange), et elles partagent toutes la même référence. Elles sont donc toutes impactées par ce changement.
Et là, tu peux légitimement te demander :
Pourquoi
{ current: 0 }
et pas juste 0 ?
Voici le même exemple qu'avant avec 0
:
value
0
0
value2
0
0
value3
0
0
0 est un number, c'est une valeur primitive. J'ai envie de dire qu'une valeur primitive est sa propre référence. Quand tu la changes, tu modifies la référence.
Une référence, c'est un state qui ne change jamais.
Finalement, dans ton code React, on peut facilement les comparer :
const [state] = useState({ current: 0 });
// Égale
const ref = useRef(0);
state.current = 1; // pas de render
ref.current = 1; // pas de render
Par ailleurs c'est pour ça que push
ne sert à rien si tu veux modifier l'état
d'un tableau avec setState
. Push ne modifie pas la ref ! C'est pour ça qu'il
faut utiliser setState([...state, value])
car dans ce cas on recrée une ref.
Voici un exemple d'un state [1,2,3]
avec un bouton qui rajoute 4
pour faire un tableau de [1,2,3,4]
:
state
[1,2,3]
[1,2,3]
[1,2,3]
On voit que quand on change le state, React crée une nouvelle référence et le state pointe sur cette nouvelle référence.
useRef vs useState vs variable
Pour finalement comprendre les différences et les subtilités de nos hooks favoris en React, voici un exemple :
let variable = 0;
const Component = () => {
const [state, setState] = useState(0);
const ref = useRef(0);
const incrementState = () => {
setState(state + 1);
};
const incrementRef = () => {
ref.current += 1;
};
const incrementVariable = () => {
variable += 1;
};
return (
<div>
<button onClick={incrementState}>Increment state ({state})</button>
<button onClick={incrementRef}>Increment ref ({ref.current})</button>
<button onClick={incrementVariable}>Increment variable ({variable})</button>
</div>
);
};
Voici le rendu pour le code ci-dessus. Amuse-toi à cliquer sur increment ref
et
increment variable
plusieurs fois puis clique sur increment state
.
ça donne quoi ? Quand tu cliques sur le useRef
rien ne se passe, pareil pour
la variable. Par contre, à l'instant où tu cliques sur le useState
la valeur du
button useRef
et de la variable change !
Pourquoi ? Car un state
provoquer un rerender qui va automatiquement mettre à
jour le composant. Lorsque le composant va être rendu, il va prendre les valeurs de
useRef
et de la variable à jour.
Les rerenders sont visibles car le composant "bip" en vert
Il faut bien comprendre que la vue n'est update que quand le composant est mis à jour.
Le useRef
permet donc de stocker une valeur, mais elle n'influe pas le render.
Mais quel est l'avantage de useRef
par rapport à une simple variable (comme dans
l'exemple au-dessus du composant) ?
Voici un deuxième exemple, mais ce qui est intéressant, c'est que cette fois-ci,
la variable
et le useRef
n'ont pas le même comportement.
Ce qui vient de se passer c'est que la variable n'est pas liée au composant. C'est une
simple variable que tu pourrais déclarer dans un fichier script.js
random.
La variable garde le même état même si c'est une instance différente.
Faire ce genre de chose est totalement déconseillé car ton composant devient impur. Les pure fonction sont des fonctions qui retournent toujours la même chose si on leur donne les mêmes paramètres. Utiliser des variables externes peut causer des bugs inattendus.
Quand se servir de useRef
useRef est parfait pour stocker des valeurs qui ne sont pas affichées dans la vue.
Comme tu l'as vu avec l'exemple des buttons, ce n'est pas très pratique d'avoir un useRef pour gérer un Counter. Normal, ce n'est pas fait pour.
Si tu veux afficher une valeur, comme la list
précédente → tu utilises un useState.
Quelques exemples concrets :
useDebounce
C'est un hook co nnu qui permet d'appeler une fonction après un délai.
Dans le cadre d'une search bar tu n'as pas envie de fetch ton API
avec la query à chaque lettre tapée. Avec le hook useDebouce
tu vas
fetch l'API quand l'utilisateur aura fini de taper depuis 1 seconde par
exemple.
Tu peux tester, écris quelque chose et l'alert
se fera 1 seconde après.
Le useRef
me permet ici de stocker la valeur retournée par le time out. Ce
qui me permet de clear dès que la fonction est rappelée. Pourquoi j'utilise un
useRef
ici ? Car je ne souhaite uniquement stocker la valeur, je ne l'affiche nulle part.
C'est possible de le faire avec useState, mais tu vas provoquer des render inutiles. Ce n'est pas la fin du monde, mais c'est comme utiliser une voiture pour faire 100 mètres.
Canvas
J'ai déjà fait un petit canva, où tu peux dessiner.
Voici le code pour ce canva :
const Canvas = () => {
const canvas = useRef(null);
const isDrawing = useRef(false);
const prevMouse = useRef(null);
const startDrawing = (event) => {
isDrawing.current = true;
prevMouse.current = getCoordinates(event, canvas.current);
};
const stopDrawing = () => {
isDrawing.current = false;
prevMouse.current = null;
};
const draw = (event) => {
if (!isDrawing.current) return;
const context = canvas.current?.getContext('2d');
// ... draw line with context
prevMouse.current = mouse;
};
return <canvas ref={canvas} /* ... all events listeners */ />;
};
Dans cet exemple j'utilise canvas
comme ref
pour garder une référence sur le canva.
Et j'utilise aussi les références isDrawing
et prevMouse
pour gérer la fonctionnalité de dessin.
En fait, c'est comme des variables dans mon code et les ref ici sont parfaites, car pour faire ce dessin j'interargis directement avec le DOM et je n'ai pas envie de faire travailler ReactDOM avec des render inutiles !
Conclusion
Dans cet article tu as compris le fonctionnement des références en JavaScript ainsi
que le fonctionnement du hook useRef
. Tu sais maintenant différencier useRef
et
useState
et tu sais même quand et où utiliser une ref
plutôt qu'un state.
Le meilleur moyen d'apprendre React !
Rejoins par développeurs, cette formation reçoit une note de 4.7 / 5 🚀
Reçois 12 leçons premium pour maîtriser React et faire partie des meilleurs