
Accéder aux noeuds du DOM (focus, mesures, intégration de bibliothèques tierces)
Apprenez comment utiliser le hook useRef en React pour obtenir une référence directe aux noeuds DOM, gérer le focus, mesurer des éléments et intégrer des bibliothèques.
Pourquoi accéder directement au DOM en React ?
React abstrait une grande partie de la manipulation directe du DOM grâce à son approche déclarative et son DOM virtuel. Vous décrivez l'état souhaité de l'UI, et React se charge de mettre à jour le DOM réel de manière efficace. Cependant, il existe des situations où cette abstraction ne suffit pas et où une interaction directe avec un noeud DOM spécifique est nécessaire. C'est l'un des cas d'usage principaux du hook `useRef`.
Les scénarios courants incluent :
- Gestion du focus : Déplacer le focus de l'utilisateur sur un champ de formulaire spécifique (par exemple, après une validation échouée ou au chargement d'une modale).
- Déclenchement d'API impératives du navigateur : Contrôler la lecture/pause d'éléments
ou, sélectionner du texte dans un input. - Mesures de position ou de taille : Obtenir les dimensions (largeur, hauteur) ou la position d'un élément dans la page après son rendu, par exemple pour positionner une infobulle ou déclencher des animations.
- Intégration de bibliothèques tierces : Utiliser des bibliothèques JavaScript qui ne sont pas spécifiquement conçues pour React et qui nécessitent une référence directe à un noeud DOM pour s'initialiser ou fonctionner (par exemple, des bibliothèques de graphiques, des éditeurs de texte enrichi, etc.).
Dans ces cas, `useRef` fournit le pont nécessaire entre le monde déclaratif de React et le monde impératif du DOM.
Le processus : Créer, Attacher, Accéder
L'accès à un noeud DOM via `useRef` suit un processus en trois étapes simples :
- Créer la référence : Dans votre composant fonctionnel, appelez `useRef` pour créer un objet ref. Il est courant de l'initialiser à `null` car le noeud DOM n'existe pas encore au moment de l'appel initial.
const monElementRef = useRef(null); - Attacher la référence : Dans votre JSX, utilisez l'attribut spécial
refsur l'élément React dont vous voulez obtenir le noeud DOM. Passez-lui l'objet ref créé à l'étape précédente.Contenu important - Accéder au noeud DOM : Une fois que React a rendu le composant et mis à jour le DOM réel, il assignera automatiquement le noeud DOM correspondant à la propriété
.currentde votre objet ref. Vous pouvez alors accéder à ce noeud viamonElementRef.currentpour appeler ses méthodes ou lire ses propriétés. Attention : cet accès doit se faire après le rendu, typiquement dans unuseEffectou un gestionnaire d'événement.
Exemple 1 : Gestion du focus
Mettons en place un champ de recherche qui reçoit automatiquement le focus lorsque le composant est monté.
import React, { useRef, useEffect } from 'react';
function SearchBar() {
const inputRef = useRef(null);
// S'exécute après le premier rendu
useEffect(() => {
// Vérifie que la ref est bien attachée au noeud DOM
if (inputRef.current) {
// Appelle la méthode DOM native .focus()
inputRef.current.focus();
}
}, []); // Tableau vide pour exécution unique au montage
return (
);
}
export default SearchBar;
Ici, `useEffect` garantit que nous essayons de mettre le focus uniquement après que l'élément `input` existe dans le DOM et que `inputRef.current` a été assigné. Le bouton utilise également la ref pour remettre le focus de manière impérative lors d'un clic.
Exemple 2 : Mesurer un élément DOM
Supposons que nous voulions afficher la largeur d'un paragraphe après son rendu.
import React, { useRef, useState, useEffect, useLayoutEffect } from 'react';
function MeasuredParagraph({ text }) {
const [width, setWidth] = useState(0);
const paragraphRef = useRef(null);
// Utilisation de useLayoutEffect pour mesurer avant que le navigateur ne peigne
// C'est souvent préférable pour éviter un flash visuel si la mesure affecte le layout
useLayoutEffect(() => {
if (paragraphRef.current) {
// Accède à une propriété DOM : offsetWidth
setWidth(paragraphRef.current.offsetWidth);
}
// La dépendance [text] assure que la mesure est refaite si le texte change
}, [text]);
return (
{text}
Largeur mesurée : {width}px
);
}
export default MeasuredParagraph;
Nous utilisons ici useLayoutEffect car la mesure se produit après le rendu mais avant que le navigateur n'ait peint l'écran. Si la mesure devait influencer la mise en page, `useLayoutEffect` est souvent préféré à `useEffect` pour éviter un possible scintillement. Nous accédons à la propriété native `offsetWidth` du noeud DOM via `paragraphRef.current`.
Exemple 3 : Intégration avec des bibliothèques tierces
Imaginez une bibliothèque de graphiques `ThirdPartyChart` qui nécessite un élément DOM pour s'initialiser :
import React, { useRef, useEffect } from 'react';
import ThirdPartyChart from 'some-chart-library'; // Bibliothèque fictive
function ChartComponent({ data }) {
const chartContainerRef = useRef(null);
const chartInstanceRef = useRef(null); // Ref pour garder l'instance de la lib
useEffect(() => {
// Initialisation au montage
if (chartContainerRef.current && !chartInstanceRef.current) {
// Passe le noeud DOM à la bibliothèque
chartInstanceRef.current = new ThirdPartyChart(chartContainerRef.current, {
// options de configuration...
});
console.log('Graphique initialisé.');
}
// Nettoyage au démontage
return () => {
if (chartInstanceRef.current) {
chartInstanceRef.current.destroy(); // Méthode de la lib pour nettoyer
chartInstanceRef.current = null;
console.log('Graphique détruit.');
}
};
}, []); // Initialisation unique
// Mise à jour lorsque les données changent
useEffect(() => {
if (chartInstanceRef.current) {
chartInstanceRef.current.update(data); // Méthode de la lib pour mettre à jour
console.log('Données du graphique mises à jour.');
}
}, [data]); // Dépendance aux données
return ;
}
export default ChartComponent;
Dans ce scénario, `useRef` (ici `chartContainerRef`) est indispensable pour fournir à la bibliothèque tierce le point de montage DOM dont elle a besoin. Une seconde ref (`chartInstanceRef`) est aussi utilisée pour conserver l'instance de la bibliothèque elle-même entre les rendus.
Conclusion : L'accès impératif contrôlé
Accéder directement aux noeuds DOM est une capacité essentielle pour certaines tâches spécifiques que le modèle déclaratif de React ne couvre pas nativement. Le hook `useRef` fournit un moyen propre et contrôlé d'obtenir ces références DOM.
Rappelez-vous que `useRef` pour l'accès DOM est une "échappatoire". Privilégiez toujours l'approche déclarative (via state et props) lorsque c'est possible. N'utilisez l'accès direct au DOM que lorsque c'est réellement nécessaire, comme pour gérer le focus, effectuer des mesures ou intégrer des systèmes externes non-React. Utilisez `useEffect` ou `useLayoutEffect` pour exécuter votre logique impérative après que la référence DOM soit disponible.