# useRef per mutare, non per stato

useRef conserva un valore mutabile senza rirenderizzare: serve per timer, identificatori e contatori interni, non per ciò che deve comparire a schermo.

> Pagina: https://ui.eneawork.it/blog/react-useref-mutazione
> Markdown: https://ui.eneawork.it/blog/react-useref-mutazione.md

*2026-11-01*

useRef conserva un valore mutabile per tutta la vita del componente senza chiedere un render: serve per identificatori, timer e contatori interni, non per ciò che deve comparire a schermo.

Il catalogo EneUI conta 111.985 componenti, 1.556 blocchi e 12.808 icone da 1.472 librerie, misurati da /api/stats il 1 novembre 2026. La cifra viene da una query all’endpoint, non da una stima: conta i record pubblicati e non dice quante ref siano usate nel modo corretto.

## Cosa cambia rispetto allo stato

Lo stato è il valore che React disegna: cambiarlo pianifica un render. La ref è una scatola con una proprietà current: scriverla non pianifica niente, e React non la legge durante il render. Per questo la ref è il posto giusto per un id di timer, un nodo del DOM o un contatore che nessuno deve vedere.

## Il caso: il campo di ricerca con debounce

import { useEffect, useRef, useState } from "react"; export function SearchBox({ onQuery }) { const timer = useRef(null); const requests = useRef(0); const [value, setValue] = useState(""); useEffect(() => { clearTimeout(timer.current); timer.current = setTimeout(() => { requests.current += 1; onQuery(value); }, 300); return () => clearTimeout(timer.current); }, [value, onQuery]); return setValue(e.target.value)} />; }

L’id del timer e il conteggio delle richieste vivono nelle ref: nessuno dei due compare nel markup, quindi nessuno dei due deve far rirenderizzare il campo a ogni tasto. Il testo digitato invece è stato, perché è quello che l’input mostra.

## Mutare un oggetto senza ricrearlo

Una ref può contenere un oggetto che sopravvive ai render: una Map usata come cache, un buffer di eventi, un flag di annullamento. La mutazione in place tiene la stessa identità, quindi nessun effetto riparte solo perché il contenitore è nuovo.

const cache = useRef(new Map()); function remember(key, value) { cache.current.set(key, value); // mutation, no render }

Il costo è nell’inizializzazione: new Map() viene valutato a ogni render e poi scartato. Per un oggetto costoso conviene inizializzarlo alla prima lettura, con un controllo su current.

## Dove la mutazione rompe

## Il comando

npm create vite@latest ref-demo -- --template react-ts cd ref-demo npm install npm run dev

Vite, distribuito con licenza MIT, crea il progetto e avvia il server di sviluppo; il componente si modifica in src/App.tsx senza toccare la configurazione.

## Record vivi nel catalogo

Tre record mostrano ref usate per il comportamento: ai-chat-input, che misura l’altezza del campo, use-image-upload, un hook costruito su una ref, e la categoria input con i campi pubblicati.

## La limitazione

Una ref non notifica nessuno: chi la legge deve sapere quando farlo, e due componenti non possono sincronizzarsi su di essa. La mutazione è anche invisibile alla timeline di React DevTools, quindi un valore sbagliato in current si trova solo leggendo il codice.

## Dove continuare

Il confine fra ref, DOM e stato è in ref, DOM e stato; l’ordine delle chiamate è in regole degli hook; il doppio avvio degli effetti in sviluppo è in useEffect due volte; il confronto fra i framework è in Vue 3 vs React.

## FAQ

### Quando uso useRef invece di useState?

Quando il valore non compare nel markup: id di timer, nodi, contatori interni. Se il render deve mostrarlo, serve useState.

### Perché useRef non fa rirenderizzare?

Perché React non legge current durante il render e non registra le scritture: la scatola resta fuori dal ciclo di aggiornamento.

### Posso mutare un oggetto dentro una ref?

Sì, fuori dal render. La mutazione in place è il motivo per cui la ref esiste, ma nel corpo del componente rompe StrictMode e il rendering concorrente.

### Perché current è null al primo render?

Perché la ref viene collegata al nodo solo dopo il mount. Leggila in un effetto o in una callback ref, non durante il render.

### useRef sostituisce una variabile globale?

No. Ogni istanza del componente ha la sua ref: non condivide dati fra componenti e non sopravvive allo smontaggio.
