12

Refs (useRef)

Accéder au DOM et stocker des valeurs mutables

Le concept : Le hook useState est conçu pour redessiner (re-render) le composant à chaque changement. Mais parfois, on a juste besoin de mémoriser une information entre les rendus en arrière-plan, sans que cela n'impacte l'interface visuellement.

C'est le rôle de useRef. Il crée une "boîte de stockage" persistante (.current). L'usage principal de useRef est de cibler directement un élément HTML réel (comme un <input>) pour forcer le focus, et plus rarement, de stocker une variable mutique (qui ne provoque pas de rendu final).

useRef() .current <input />
💡 Deux usages de useRef

useRef retourne un objet mutable { current: valeur }. Il a deux usages distincts et importants :

  1. Référencer un nœud DOM directement (focus, play/pause vidéo, mesures, animations).
  2. Stocker une valeur persistante entre les rendus sans déclencher de re-rendu (contrairement à useState).

ref.current peut être modifié librement — React ne surveille pas sa valeur et ne relancera jamais un rendu à cause de ref.current.

1. Accès direct au DOM

Focus, scroll, media
import { useRef } from 'react';

function SearchBar() {
    const inputRef = useRef(null);

    const focusInput = () => {
        inputRef.current.focus(); // Accès direct DOM
        inputRef.current.select(); // Sélectionner le texte
    };

    return (
        <>
            <input ref={inputRef} type="text" />
            <button onClick={focusInput}>Focus</button>
        </>
    );
}

// Contrôler une vidéo
function VideoPlayer() {
    const videoRef = useRef(null);
    return (
        <>
            <video ref={videoRef} src="film.mp4" />
            <button onClick={() => videoRef.current.play()}>▶️</button>
            <button onClick={() => videoRef.current.pause()}>⏸️</button>
        </>
    );
}
Mesures DOM (dimensions, position)
function MeasureBox() {
    const boxRef  = useRef(null);
    const [size, setSize] = useState({});

    useEffect(() => {
        const { width, height } =
            boxRef.current.getBoundingClientRect();
        setSize({ width, height });
    }, []);

    return (
        <div ref={boxRef}>
            Je mesure {size.width}px × {size.height}px
        </div>
    );
}

2. Persister une valeur sans re-rendu

ref.current comme "instance variable"
// Stocker un timer ID entre les rendus
function AutoSave({ data }) {
    const timerRef = useRef(null);

    useEffect(() => {
        // Annuler le timer précédent à chaque frappe
        clearTimeout(timerRef.current);

        // Sauvegarder après 2 secondes sans frappe
        timerRef.current = setTimeout(() => {
            saveData(data);
        }, 2000);

        return () => clearTimeout(timerRef.current);
    }, [data]);
}
Valeur précédente (usePrevious)
// Custom Hook : accéder à la valeur précédente
function usePrevious(value) {
    const prevRef = useRef();

    useEffect(() => {
        prevRef.current = value;
    }); // Pas de deps = après chaque rendu

    return prevRef.current;
}

// Utilisation
const prevCount = usePrevious(count);
// Afficher "Avant: 4, Maintenant: 5"

3. forwardRef — Passer une ref à un composant

forwardRef + useImperativeHandle
// forwardRef : donner accès au DOM d'un composant enfant
const FancyInput = forwardRef((props, ref) => (
    <input className="fancy" ref={ref} {...props} />
));

// useImperativeHandle : exposer des méthodes personnalisées
const Dialog = forwardRef((props, ref) => {
    const [open, setOpen] = useState(false);

    useImperativeHandle(ref, () => ({
        show: () => setOpen(true),
        hide: () => setOpen(false),
    }));

    return open ? <div className="dialog">{props.children}</div> : null;
});

// Utilisation : contrôle imperatif du Dialog
const dialogRef = useRef();
<Dialog ref={dialogRef}>Contenu</Dialog>
<button onClick={() => dialogRef.current.show()}>Ouvrir</button>
⚠️ Règle d'or : N'utilisez les refs DOM que pour des cas impératifs que React ne peut pas gérer déclarativement (focus, play/pause, animations canvas...). Préférez toujours le state déclaratif pour contrôler ce qui s'affiche.