Contactez-nous

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 :

  1. 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);
    
  2. Attacher la référence : Dans votre JSX, utilisez l'attribut spécial ref sur l'élément React dont vous voulez obtenir le noeud DOM. Passez-lui l'objet ref créé à l'étape précédente.
    Contenu important
  3. 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é .current de votre objet ref. Vous pouvez alors accéder à ce noeud via monElementRef.current pour appeler ses méthodes ou lire ses propriétés. Attention : cet accès doit se faire après le rendu, typiquement dans un useEffect ou 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.