Hydration mismatch in React: i bug che compaiono solo quando aggiungi SSR
Tema, localStorage, date e rendering deterministico: quattro lezioni pratiche.
L'hydration mismatch è uno di quei problemi che puoi non incontrare mai finché React renderizza tutto nel browser.
Con SSR, invece, il browser riceve HTML prodotto altrove e React deve riuscire a ricostruire esattamente lo stesso albero al primo render.
Che cos'è davvero l'hydration
Il server genera markup statico. Nel browser React non dovrebbe ricrearlo da zero: deve agganciarsi a quell'HTML, collegando stato ed eventi.
Se però il primo render client produce un risultato differente, React segnala un mismatch e può essere costretto a scartare parte del markup server-side.
Caso 1: localStorage nel primo render
Il server non ha localStorage. Il browser sì.
Se un componente usa una preferenza salvata per decidere immediatamente cosa renderizzare, server e client possono partire da due stati differenti.
Un theme switcher è l'esempio perfetto: il server può assumere il tema di sistema, mentre il browser conosce già la scelta dell'utente.
La soluzione: primo render deterministico
La strategia è semplice: il primo render client deve usare lo stesso valore del server. Solo dopo il mount si leggono preferenze locali e si aggiorna lo stato.
Questo può introdurre una piccola fase di sincronizzazione, ma evita di rompere l'hydration dell'intera pagina.
Caso 2: il fuso orario
Questo bug è più subdolo perché non coinvolge API browser.
Una data formattata con le impostazioni locali del processo può produrre risultati differenti se Node gira in UTC e il browser usa un altro fuso.
Vicino alla mezzanotte la differenza può diventare visibile: il server mostra un giorno, il browser quello successivo.
Caso 3: contenuto dipendente dall'ambiente
Lo stesso rischio esiste con:
window.innerWidth;matchMedia();- lingua del browser;
- cookie letti soltanto lato client;
Date.now();- valori casuali.
Tutto ciò che può produrre valori differenti tra server e browser va trattato con attenzione.
useEffect non è il nemico
Il problema non è usare API browser in assoluto. È usarle durante il render iniziale.
Un accesso a window dentro un useEffect che gira dopo l'hydration è normalmente sicuro. Un accesso dentro l'inizializzatore di uno useState può invece modificare immediatamente il markup.
Come testare i mismatch
Un test puramente unitario non basta sempre. Serve almeno una prova che confronti il comportamento server/client e, idealmente, un browser reale automatizzato.
Un buon controllo consiste nel:
- generare l'HTML via SSR;
- caricare quella pagina in un browser;
- impostare stati realistici, come preferenze salvate;
- controllare console error e warning durante l'hydration.
Conclusione
L'hydration mismatch non è un difetto misterioso di React. Quasi sempre rivela una cosa precisa: il primo render dipende da informazioni che server e browser non condividono.
SSR costringe quindi a distinguere nettamente tra stato necessario a costruire l'HTML iniziale e stato che può essere applicato successivamente.