React
Batching automatico degli aggiornamenti
Da React 18 gli aggiornamenti si raggruppano anche dentro promise, timeout e listener nativi: due setState nello stesso tick producono un solo render.
In sintesi
- Prima di React 18 il batching valeva solo dentro gli handler React; dalla 18 vale ovunque.
- flushSync forza il render immediato quando il codice deve leggere il layout subito dopo l’aggiornamento.
- Un solo render significa che gli stati intermedi non vengono mai dipinti.
Da React 18 gli aggiornamenti di stato si raggruppano anche dentro promise, timeout e listener nativi: due setState nello stesso tick producono un solo render invece di due.
Cosa è cambiato rispetto a React 17
Fino alla 17 il raggruppamento valeva solo dentro gli handler React: un setTimeout o un then produceva un render per ogni aggiornamento. Dalla 18 il comportamento è identico in ogni contesto, compresi i listener aggiunti con addEventListener.
Il caso asincrono
function Counter() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
async function handleClick() {
await Promise.resolve(); // outside a React event handler
setCount((c) => c + 1);
setFlag((f) => !f); // one render for both updates
}
return (
{count} {String(flag)}
);
}
Nel codice sopra il secondo render non esiste: React applica le due modifiche e disegna una volta. Il valore letto dal DOM dopo l’await è ancora quello precedente, perché il render non è ancora avvenuto.
Quando il batching va disattivato
flushSync forza l’aggiornamento prima della riga successiva. Serve quando il codice deve misurare il layout subito dopo, per esempio per posizionare un tooltip sulla nuova altezza di un elemento.
import { flushSync } from "react-dom";
function addAndMeasure() {
flushSync(() => setCount((c) => c + 1));
const height = listRef.current.getBoundingClientRect().height;
return height;
}
Il costo è un render in più, quindi flushSync resta un’eccezione. React avvisa anche quando viene chiamato durante il render o dentro un ciclo di vita, perché lì il render sincrono non è ammesso.
Il pattern che rompe
Chi legge lo stato fra due setState ottiene il valore vecchio: la variabile della closure non cambia finché il componente non viene richiamato. Le soluzioni sono due, l’aggiornamento funzionale e un ref che porta il valore fuori dal render.
Il comando
Il batching automatico esiste dalla 18; il comando installa la major corrente e verifica che il progetto non sia rimasto su una versione precedente.
npm install react@19 react-dom@19
npm ls react-dom
Record vivi nel catalogo
Tre record che aggiornano più valori insieme: animated-counter anima un numero con più stati, card-status-list cambia stato e badge nella stessa azione, project-data-table aggiorna filtro e pagina insieme. Sono record fra i 111.985 componenti e i 1.556 blocchi.
Fonte e licenza
React è distribuito con licenza MIT. Le note di rilascio della versione 18 descrivono il batching automatico come modifica predefinita, senza flag da attivare; flushSync è documentato su react.dev come uscita di sicurezza.
La limitazione
Il batching riduce il numero di render, non il lavoro per render: un calcolo costoso eseguito nel corpo del componente gira una volta con lo stato finale, quindi il costo resta. In sviluppo StrictMode invoca due volte alcune funzioni e i contatori nei log sembrano sbagliati.
Dove continuare
Le novità della major sono in React 19; le doppie chiamate in sviluppo sono in StrictMode; il confronto con il modello di Vue è in Vue 3 vs React.
Domande frequenti
Il batching vale anche per gli aggiornamenti dentro useEffect?
Sì dalla 18: gli aggiornamenti programmati in un effetto si raggruppano con quelli dello stesso ciclo. Prima della 18 ognuno produceva un render separato.
Un solo render significa un solo commit?
Sì, un render porta a un commit. Il batching riduce entrambi, ma non riduce il lavoro fatto dai calcoli che stanno dentro il componente.
Quando serve flushSync?
Quando il codice deve leggere il DOM aggiornato subito dopo il setState, per esempio per misurare un elemento prima di posizionare un altro. Nel caso normale non serve.
Il batching rompe i test?
No, ma cambia il numero di render osservati. Un test che conta le chiamate a una funzione nel corpo del componente ne vede meno, perché il componente viene richiamato una volta.
Il comportamento cambia in React 19?
No, React 19 mantiene il batching automatico introdotto nella 18. Cambiano altre cose, come ref nelle props e le azioni, non questa.