useRef restituisce un oggetto, { current: initialValue }, che React conserva per tutta la vita del componente. Cambiare ref.current non provoca alcun nuovo render. Questo rende i ref adatti a due compiti: tenere un nodo DOM così da poterci chiamare metodi come focus(), e ricordare tra un rendering e l'altro un valore che lo schermo non ha bisogno di mostrare.
Passare l'oggetto ref a ref={inputRef} dice a React di mettere il nodo DOM dell'<input> in inputRef.current. Cambia focus() in select(), scrivi qualcosa e clicca di nuovo: il testo viene evidenziato.
La sintassi
const myRef = useRef(initialValue);
myRef.current; // read
myRef.current = value; // write, no re-render
initialValueviene usato solo al primo rendering. Per i ref al DOM ènull.- React restituisce lo stesso oggetto a ogni rendering. Cambia solo
current. - Leggere e scrivere
currentè normale JavaScript. React non lo traccia.
Un valore che non provoca rendering
Lo stato serve per ciò che lo schermo mostra. Un ref serve per ciò che il componente deve ricordare ma non mostrare. Questo blocco tiene un contatore in un ref e uno nello stato, e li registra entrambi:
Clicca Ref + 1 qualche volta. La console mostra ref.current che sale, ma la pagina dice ancora 0 e non viene registrato alcun rendering. Ora clicca State + 1: il componente si renderizza, e il contatore del ref sullo schermo salta al numero che il ref conteneva già. Il ref ha mantenuto il suo valore per tutto il tempo; React semplicemente non aveva motivo di disegnarlo.
Ref vs stato
useState | useRef | |
|---|---|---|
| Restituisce | [value, setValue] | { current: value } |
| Cambiarlo | Renderizza di nuovo il componente | Non produce nulla di visibile |
| Quando il nuovo valore è leggibile | Nel rendering successivo | Subito |
| Lo cambi con | Il setter | Un'assegnazione, ref.current = x |
| Si usa per | Tutto ciò che lo schermo mostra | Id di timer, nodi DOM, valori precedenti, flag |
Se hai dubbi, chiediti se il JSX legge il valore. Se lo legge, va nello stato.
Memorizzare l'id di un timer
setInterval restituisce un id che ti serve più tardi per fermare il timer. È perfetto per un ref: il gestore Stop ne ha bisogno, ma lo schermo non lo mostra mai, e tenerlo in una semplice variabile lo perderebbe al rendering successivo.
Sostituisci useRef(null) con let intervalRef = { current: null }; e prova Start, aspetta due secondi, poi Stop: il timer continua. Ogni tick renderizza il componente, che crea un oggetto nuovo, quindi Stop non cancella nulla.
Ricordare il valore precedente
Un effetto viene eseguito dopo il rendering, quindi può confrontare il nuovo valore con quello memorizzato in un ref, e poi salvare quello nuovo per la volta successiva:
Clicca green, poi blue: la console registra changed from red to green e changed from green to blue. Cliccare il colore già selezionato non registra nulla, perché l'effetto viene eseguito solo quando color cambia.
Lavorare con il DOM
I ref sono il modo per chiamare API del browser per cui React non ha una prop: focus(), select(), scrollIntoView(), play() su un video, oppure leggere una dimensione con getBoundingClientRect().
const lastItemRef = useRef(null);
function scrollToLast() {
lastItemRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
<li ref={lastItemRef}>Last item</li>
ref.current è null durante il primo rendering, perché React non ha ancora creato il nodo DOM, e di nuovo dopo che l'elemento è stato rimosso. Leggilo nei gestori di eventi e negli effetti, dove il nodo esiste. Per misurare un elemento e posizionare qualcosa prima che il browser disegni la pagina, leggi il ref in useLayoutEffect.
Non leggere né scrivere ref.current durante il rendering
React si aspetta che il corpo del componente sia un calcolo puro: stesse props e stesso stato, stesso JSX. Un ref cambia al di fuori di questo modello, quindi un rendering che lo legge o lo scrive può mostrare risultati diversi per gli stessi input, e React potrebbe chiamare il corpo più spesso di quanto ti aspetti.
function Bad() {
const count = useRef(0);
count.current++; // writing during render
return <p>{count.current}</p>; // reading during render
}
Leggi e scrivi i ref nei gestori di eventi e negli effetti. L'unica eccezione è l'inizializzazione lazy, in cui riempi il ref una volta al primo rendering:
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new VideoPlayer(); // runs once
}
(La demo del contatore con ref qui sopra legge clicks.current nel JSX di proposito, per mostrare che lo schermo non lo segue. Non copiarlo nel codice reale.)
Ref callback
Invece di un oggetto ref, puoi passare una funzione a ref. React la chiama con il nodo DOM quando l'elemento viene aggiunto. In React 19 la funzione può restituire una funzione di cleanup, che React chiama quando l'elemento viene rimosso. È comodo per una lista, dove non puoi chiamare useRef una volta per ogni voce:
Al caricamento la console registra una riga attached per ogni voce. Clicca Remove last e compare solo detached Cheese. trackNode è definita fuori dal componente, quindi è la stessa funzione a ogni rendering e React lascia stare le altre voci. Scrivila invece in linea come ref={(node) => { ... }} e ogni clic stacca e riattacca ogni voce, perché ogni rendering passa una funzione nuova.
Passare un ref al tuo componente
In React 19, ref è una prop normale per i function component. Il genitore crea il ref, e il figlio lo mette sull'elemento DOM che vuole esporre:
Prima di React 19 questo richiedeva forwardRef, che funziona ancora. La pagina su forwardRef mostra entrambe le versioni e come esporre solo alcuni metodi con useImperativeHandle.
Errori comuni
Mostrare il valore di un ref nel JSX. Lo schermo non lo segue. Se l'utente deve vedere il valore, usa lo stato.
Usare ref.current come dipendenza di un effetto. [ref.current] legge il valore durante il rendering e React non sa quando cambia, quindi l'effetto non viene rieseguito quando succede. Usa un ref callback per reagire al collegamento di un nodo.
Leggere un ref al DOM durante il primo rendering. È null finché React non ha creato il nodo. Leggilo in un effetto o in un gestore.
Creare un nuovo oggetto invece di usare l'hook. const ref = { current: null } nel corpo del componente è un oggetto nuovo a ogni rendering. Solo useRef ti dà ogni volta lo stesso.
Domande frequenti
Cosa restituisce useRef?
Un semplice oggetto con una sola proprietà, current, impostata sul valore iniziale che passi. React restituisce lo stesso oggetto a ogni rendering, quindi qualsiasi cosa metti in current è ancora lì la volta successiva.
Qual è la differenza tra useRef e useState?
Cambiare lo stato fa renderizzare di nuovo il componente; cambiare ref.current no. Usa lo stato per i valori che lo schermo mostra, e un ref per i valori che servono solo ai tuoi gestori di eventi ed effetti, come l'id di un timer o un nodo DOM.
Perché ref.current è null?
React imposta ref.current sul nodo DOM dopo averlo creato, durante il commit. Durante il primo rendering è ancora null, e torna null dopo che l'elemento è stato rimosso. Leggilo in un gestore di eventi o in un effetto.
Cambiare ref.current provoca un nuovo render?
No. React non sa che lo hai cambiato. Il nuovo valore compare sullo schermo solo se qualcos'altro, come un aggiornamento di stato, renderizza di nuovo il componente.
Come passo un ref a un componente figlio?
In React 19 un function component riceve ref come una prop normale, quindi passi <MyInput ref={inputRef} /> e il figlio lo mette sul suo <input>. Le versioni precedenti richiedevano forwardRef.