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).
💡 Deux usages de useRef
useRef retourne un objet mutable { current: valeur }. Il a deux usages distincts et importants :
- Référencer un nœud DOM directement (focus, play/pause vidéo, mesures, animations).
- 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
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> </> ); }
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
// 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]); }
// 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 : 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>