# Chapitre 3 : useRef ## Deux usages de useRef useRef a deux usages principaux : 1. **Accéder à un élément DOM** (input, div, etc.) 2. **Stocker une valeur qui persiste** entre les rendus sans déclencher de re-rendu ## Accéder au DOM ### Exemple : Focus sur un input ```javascript import { useRef } from 'react'; function SearchForm() { const inputRef = useRef(null); const handleClick = () => { inputRef.current.focus(); }; return (
); } ``` ### Comment ça marche 1. `useRef(null)` crée un objet `{ current: null }` 2. `ref={inputRef}` dit à React de mettre l'élément DOM dans `inputRef.current` 3. Après le rendu, `inputRef.current` contient l'élément `` ### Exemple : Scroll vers un élément ```javascript function ScrollToSection() { const sectionRef = useRef(null); const scrollToSection = () => { sectionRef.current.scrollIntoView({ behavior: 'smooth' }); }; return (
{/* ... beaucoup de contenu ... */}

Section cible

); } ``` ### Exemple : Mesurer un élément ```javascript function MeasuredBox() { const boxRef = useRef(null); const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); useEffect(() => { if (boxRef.current) { const { width, height } = boxRef.current.getBoundingClientRect(); setDimensions({ width, height }); } }, []); return (
Contenu de la boîte

Dimensions : {dimensions.width} x {dimensions.height}

); } ``` ## Stocker une valeur persistante ### Différence avec useState ^ Critère ^ useState ^ useRef ^ | Persiste entre rendus | Oui | Oui | | Déclenche un re-rendu | Oui | Non | | Accès à la valeur | `state` | `ref.current` | ### Exemple : Compter les rendus ```javascript function RenderCounter() { const [count, setCount] = useState(0); const renderCount = useRef(0); // Incrémenté à chaque rendu, sans causer de re-rendu renderCount.current += 1; return (

Count : {count}

Rendus : {renderCount.current}

); } ``` ### Exemple : Valeur précédente ```javascript function usePrevious(value) { const ref = useRef(); useEffect(() => { ref.current = value; }, [value]); return ref.current; } function Counter() { const [count, setCount] = useState(0); const previousCount = usePrevious(count); return (

Actuel : {count}

Précédent : {previousCount}

); } ``` ### Exemple : Éviter les re-exécutions de useEffect ```javascript function Timer({ onTick }) { const onTickRef = useRef(onTick); // Met à jour la ref sans déclencher de re-rendu useEffect(() => { onTickRef.current = onTick; }, [onTick]); useEffect(() => { const interval = setInterval(() => { onTickRef.current(); // Utilise toujours la dernière version }, 1000); return () => clearInterval(interval); }, []); // Pas besoin de onTick dans les dépendances } ``` ## Cas d'usage avec formulaires non contrôlés Parfois, il est plus simple de lire la valeur du DOM directement : ```javascript function UncontrolledForm() { const nameRef = useRef(null); const emailRef = useRef(null); const handleSubmit = (e) => { e.preventDefault(); console.log({ name: nameRef.current.value, email: emailRef.current.value }); }; return (
); } ``` **Note** : les formulaires contrôlés (avec useState) sont généralement préférés car ils permettent la validation en temps réel. ## Attention : ne pas lire/écrire pendant le rendu ```javascript // INCORRECT function BadComponent() { const ref = useRef(0); ref.current += 1; // Modification pendant le rendu return
{ref.current}
; } // CORRECT - modifier dans useEffect ou gestionnaire d'événement function GoodComponent() { const ref = useRef(0); useEffect(() => { ref.current += 1; // OK dans useEffect }); const handleClick = () => { ref.current += 1; // OK dans gestionnaire }; return
...
; } ``` ## Exercices ### Exercice 1 : Auto-focus au montage Créer un formulaire de login qui met automatiquement le focus sur le champ email au chargement. **Solution :** ```javascript function LoginForm() { const emailRef = useRef(null); useEffect(() => { emailRef.current.focus(); }, []); return (
); } ``` ### Exercice 2 : Chronomètre avec pause Créer un chronomètre avec boutons Start/Pause/Reset. **Solution :** ```javascript function Stopwatch() { const [time, setTime] = useState(0); const [isRunning, setIsRunning] = useState(false); const intervalRef = useRef(null); useEffect(() => { if (isRunning) { intervalRef.current = setInterval(() => { setTime(t => t + 1); }, 1000); } return () => { if (intervalRef.current) { clearInterval(intervalRef.current); } }; }, [isRunning]); const start = () => setIsRunning(true); const pause = () => setIsRunning(false); const reset = () => { setIsRunning(false); setTime(0); }; return (

{time} secondes

); } ``` ## Points clés à retenir 1. **useRef pour le DOM** : `ref={myRef}` puis `myRef.current` 2. **useRef pour persister** : stocke une valeur sans re-rendu 3. **Ne pas modifier pendant le rendu** : seulement dans useEffect ou événements 4. **Valeur dans `.current`** : `ref.current` et non `ref` [[:15_training:module3-hooks-fondamentaux:useeffect|← Chapitre précédent]] | [[:15_training:module3-hooks-fondamentaux:start|Retour au module]] | [[:15_training:module3-hooks-fondamentaux:usecontext|Chapitre suivant : useContext →]]