Nel 1789, Antoine Lavoisier mise per iscritto uno dei pilastri della chimica moderna:
"Nulla si crea, nulla si distrugge, tutto si trasforma". È la legge della conservazione della massa. Ogni atomo coinvolto in una reazione deve essere bilanciato. Se bruci della carta, la cenere e i gas prodotti peseranno esattamente quanto la carta e l'ossigeno originali.
Nel mondo magico di JavaScript e React, tendiamo a ignorare la chimica. Pensiamo di poter creare componenti, farli apparire sullo schermo, e poi farli sparire nel nulla chiudendo un modale. Ma i byte nella RAM sono fisici. Se non li "trasformi" pulendoli, ottieni il nemico letale delle Single Page Applications: il Memory Leak.
L'Accumulo Inesorabile
Browser moderni come Chrome hanno un Garbage Collector (un netturbino automatico) che fa un lavoro eccellente nel buttare via i dati orfani. Ma il Collector non è onnisciente.
Se lasci un Event Listener o un timer attivo agganciato a un componente smontato, il browser penserà: "Ehi, questo dev sta ancora ascoltando l'evento, meglio tenere tutto in memoria". E così, click dopo click, l'app inizia a pesare come una nana bianca, finché la tab non implode.
Il Fango: Event Listeners Immortali
Durante un tirocinio aziendale, mi chiesero di fare una barra laterale che sparisse o apparisse facendo il resize dello schermo. Avevo mezz'ora prima della call di allineamento, così l'ho buttata giù in fretta in React attaccando un ascoltatore globale alla window.
// Bad Code: Sto ignorando Lavoisier
function Sidebar() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
// Creo massa (un listener) dal nulla
window.addEventListener('resize', () => {
setWidth(window.innerWidth);
console.log("Ridimensiono!");
});
}, []);
return <div>Larghezza: {width}px</div>;
}
Il dramma? Quella sidebar era dentro una dashboard dove gli utenti navigavano tra decine di sezioni diverse, facendola montare e smontare in continuazione. Ogni mount creava un nuovo listener fantasma in memoria senza mai distruggere quello vecchio.
Dopo un'ora di utilizzo, un singolo resize della finestra scatenava centinaia di esecuzioni contemporanee della funzione. I browser dell'amministrazione aziendale giravano con le ventole che sembravano elicotteri.
L'Ingegneria: Pulire le Proprie Ceneri
La soluzione corretta rispetta il bilancio energetico della memoria. Se nel tuo useEffect "crei" qualcosa (un listener, un setInterval, una WebSocket), sei obbligato a fornire una funzione di cleanup per distruggerla alla chiusura.
// Good Code: Conservazione della Memoria
function Sidebar() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
// Assegno la funzione specificatamente per poterla rintracciare
window.addEventListener('resize', handleResize);
// Return the Cleanup Function: il mio netturbino personale
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return <div>Larghezza: {width}px</div>;
}
Il Takeaway
Non fidatevi mai ciecamente del Garbage Collector. Voi siete gli ingegneri creatori dell'evento, voi avete la responsabilità dello smantellamento.
Ricordatevi di Lavoisier la prossima volta che vi iscrivete a uno stream di dati in un componente React. Se non predisponete un piano di evacuazione, le vostre omissioni si accumuleranno silenziosamente nella RAM degli utenti. E Chrome non perdona.