useSyncExternalStore consente a un componente di leggere dati che risiedono al di fuori di React senza rischiare i glitch visivi che il rendering concorrente può introdurre. Sostituisce il vecchio pattern di inserimento dei valori esterni nello stato locale tramite useEffect, un pattern che può causare il "tearing" – ovvero parti dell'interfaccia utente che mostrano valori diversi durante lo stesso render.

Perché il vecchio pattern fallisce in modalità concorrente

Prima di React 18, la ricetta comune era:

const [value, setValue] = useState(initial);
useEffect(() => {
  const unsubscribe = externalSource.subscribe(v => setValue(v));
  return unsubscribe;
}, []);

L'effetto viene eseguito dopo che il componente è stato renderizzato. Quando il renderer concorrente di React decide di mettere in pausa, annullare o ripetere un render, l'effetto potrebbe attivarsi in un punto diverso rispetto all'interfaccia utente che legge lo stato. Il risultato è un disallineamento: un componente potrebbe aver letto il vecchio valore, un altro quello nuovo, e lo schermo mostra brevemente dati incoerenti. Questo "tearing" è innocuo in un mondo puramente sincrono, ma diventa un bug visibile quando React renderizza più versioni dell'interfaccia utente in parallelo.

Come useSyncExternalStore risolve il problema

L'hook richiede due callback:

  • subscribe – registra un listener e restituisce una funzione per rimuoverlo.
  • getSnapshot – restituisce il valore corrente della sorgente esterna.

React chiama subscribe quando il componente viene montato e lo rimuove al dismount. Durante ogni render, invoca getSnapshot e garantisce che tutti i componenti che utilizzano lo stesso store esterno leggano esattamente lo stesso snapshot. Poiché la lettura avviene all'interno della fase di render, il renderer può coordinare gli aggiornamenti in tutto l'albero, eliminando il tearing.

Librerie come Zustand e Redux avvolgono già i propri store con questo hook, motivo per cui funzionano immediatamente con le funzionalità concorrenti di React 18.

Regole per evitare malfunzionamenti dell'hook

  1. Restituisci solo ciò di cui il componente ha bisogno – un oggetto grande costringe React a confrontare strutture profonde a ogni aggiornamento. Uno snapshot ristretto mantiene economica la rilevazione dei cambiamenti.
  2. Preserva l'identità referenziale – se il valore esterno non è cambiato, getSnapshot deve restituire lo stesso riferimento dell'oggetto. Restituire un nuovo oggetto a ogni chiamata innesca un loop di render infinito perché React rileva un cambiamento a ogni render.
  3. Limita l'uso alle sorgenti esterne – lo stato locale del componente appartiene a useState o useReducer. Utilizzare l'hook per store esterni per valori puramente interni aggiunge un'indirezione non necessaria.

Quando ricorrere a useSyncExternalStore

  • Avvolgere le API del browser (dimensione della finestra, stato della rete, media query).
  • Collegare uno store JavaScript semplice che non conosce React.
  • Sostituire il prop-drilling con una sorgente globale di sola lettura a cui si iscrivono molti componenti.

L'argomento contrario

Il pattern useState + useEffect funziona ancora per dati semplici che cambiano di rado, specialmente quando un'applicazione non abilita le funzionalità concorrenti. È un approccio di livello inferiore che alcuni sviluppatori trovano più facile da comprendere perché la logica di iscrizione risiede esplicitamente nell'effetto. Tuttavia, il compromesso è il rischio di tearing una volta attivato il rendering concorrente. Per i progetti che utilizzano già le nuove capacità di React 18, la sicurezza extra di useSyncExternalStore supera il minimo boilerplate aggiuntivo.

Cosa monitorare successivamente

  • Verifica che il getSnapshot del tuo store restituisca un riferimento stabile; un rapido console.log del riferimento durante lo sviluppo può esporre la ricreazione accidentale di oggetti.
  • Mantieni la funzione di iscrizione leggera – i compiti pesanti dovrebbero avvenire all'interno della sorgente esterna, non nella callback passata a React.
  • Testa i componenti in modalità concorrente di React (ad esempio, usando act con ReactDOM.createRoot) per confermare che non compaia alcun tearing.

Una demo live mostra l'hook in azione, sincronizzando un semplice contatore memorizzato al di fuori di React e aggiornando più componenti senza glitch visivi. La demo è disponibile all'indirizzo https://usesyncexternalstore.vercel.app/. Il codice sorgente si trova su GitHub e una spiegazione più approfondita illustra i dettagli dell'implementazione.

In sintesi: In un mondo in cui React può renderizzare parti dell'interfaccia utente fuori ordine, useSyncExternalStore è il ponte affidabile tra i dati esterni e il rendering dei componenti. Preserva la coerenza visiva, si adatta agli store globali ed è già integrato nelle popolari librerie di gestione dello stato. Adottalo ovunque tu debba leggere uno stato non appartenente a React, e mantieni gli snapshot piccoli e stabili per trarne il massimo beneficio.