React è una delle librerie JavaScript più popolari per la creazione di interfacce utente. Spesso, nella creazione delle interfacce, potrebbe essere necessario eseguire effetti collaterali, come recuperare dati da un’API, rimanere in attesa di eventi o manipolare il DOM.

È qui che torna utile useEffect, un hook di React che permette di gestire queste operazioni in modo dichiarativo ed efficiente, garantendo che l’interfaccia utente rimanga reattiva e aggiornata.

Comprendere il funzionamento di useEffect è essenziale per creare applicazioni robuste e dinamiche, tanto per chi è alle prime armi quanto per sviluppatori più esperti in React. In questo articolo spiegheremo come funziona l’hook useEffect e come usarlo nei progetti React.

Cos’è un effetto collaterale in React?

Quando si lavora con i componenti di React, a volte è necessario interagire con entità o eseguire azioni al di fuori dell’ambito di React. Queste interazioni esterne sono note come effetti collaterali, o più brevemente effetti.

In React, la maggior parte dei componenti sono funzioni pure, cioè ricevono input (props) e producono output prevedibili (JSX), come si vede nell’esempio che segue:

export default function App() {
  return <User userName="JaneDoe" />   
}
 
function User(props) {
  return <h1>{props.userName}</h1>; // John Doe
}

Tuttavia, gli effetti collaterali non sono prevedibili perché coinvolgono interazioni al di fuori dell’ambito abituale di React.

Si pensi al caso in cui si desidera cambiare dinamicamente il titolo della scheda del browser per visualizzare il sito userName dell’utente. Anche se potrebbe essere allettante farlo direttamente all’interno del componente, non è l’approccio consigliato perché è considerato un effetto collaterale:

const User = ({ userName }) => {
  document.title = `Hello ${userName}`; // ❌ Non fatelo mai nel corpo del componente: è un effetto collaterale.

  return <h1>{userName}</h1>;
}

Eseguire effetti collaterali direttamente nel corpo del componente può interferire con il processo di rendering del componente React.

Per evitare interferenze, è bene separare gli effetti collaterali in modo che vengano eseguiti o funzionino solo dopo il rendering del componente, assicurando una chiara separazione tra il processo di rendering e le interazioni esterne necessarie. Questa separazione avviene con l’hook useEffect.

Le basi di useEffect

L’hook useEffect è progettato per imitare i metodi del ciclo di vita come componentDidMount, componentDidUpdate e componentWillUnmount presenti nei componenti di classe.

Prima di utilizzare useEffect, bisogna importarlo da “react” e poi invocarlo all’interno di un componente funzionale (al livello superiore del componente). Richiede due argomenti: una funzione di callback e un array opzionale di dipendenze.

useEffect(callbackFn, [dependencies]);

Questo può essere scritto meglio come:

useEffect(() => {
  // codice da eseguire quando è attivato l'effetto
}, [dependencies]);
  • La funzione di callback contiene il codice da eseguire quando il componente viene reso o il valore della dipendenza cambia. È qui che si eseguono gli effetti collaterali.
  • L’array delle dipendenze specifica i valori che devono essere monitorati per le modifiche. La funzione di callback verrà eseguita quando un qualsiasi valore di questo array cambia.

Per esempio, è possibile correggere l’esempio precedente in modo da eseguire correttamente l’effetto collaterale all’interno di un hook useEffect:

import { useEffect } from 'react';

const User = ({ userName }) => {
  useEffect(() => {
	document.title = `Hello ${userName}`;
  }, [userName]);

  return <h1>{userName}</h1>;   
}

Nell’esempio precedente, l’hook useEffect sarà invocato dopo il rendering del componente e ogni volta che la dipendenza – il valore di userName – cambia.

Le dipendenze in useEffect

Le dipendenze giocano un ruolo fondamentale nel controllo dell’esecuzione di useEffect. Si tratta del secondo argomento dell’hook useEffect.

useEffect(() => {
  // codice da eseguire quando è attivato l'effetto
}, [dependencies]);

Un array di dipendenze vuoto [] fa sì che l’effetto venga eseguito una sola volta, simulando componentDidMount. Specificando correttamente le dipendenze, l’effetto si aggiorna quando cambiano determinati valori, in modo simile a componentDidUpdate.

Nota: è necessario prestare attenzione quando si hanno dipendenze complesse. Selezionando con attenzione i valori da includere nell’array delle dipendenze, è possibile evitare aggiornamenti non necessari.

Se si omette del tutto l’array di dipendenze, l’effetto verrà eseguito ogni volta che il componente viene renderizzato, con conseguenti problemi di prestazioni.

useEffect(() => {
  // codice da eseguire quando è attivato l'effetto
});

In React, capire come funziona il rendering è un grande vantaggio perché permette di comprendere l’importanza dell’array di dipendenze.

Come funziona il rendering in React?

In React, il rendering genera l’interfaccia utente (UI) in base allo stato attuale e alle proprietà di un componente. Il rendering avviene in diverse situazioni. Il rendering iniziale avviene quando un componente viene renderizzato o montato per la prima volta.

Inoltre, una modifica di state o props di un componente attiva un nuovo rendering per garantire che l’interfaccia utente tenga conto dell’aggiornamento dei valori. Le applicazioni React sono create con una struttura ad albero di componenti che formano una gerarchia. Durante il rendering, React parte dal componente principale e renderizza ricorsivamente i suoi componenti figli.

Ciò significa che, se si verifica una modifica nel componente principale, tutti i componenti vengono renderizzati. È importante notare che richiamare gli effetti collaterali (che nella maggior parte dei casi sono funzioni complesse) a ogni rendering può essere dispendioso. Per ottimizzare le prestazioni, si può usare l’array di dipendenze nell’hook useEffect per specificare quando deve essere attivato, limitando i rendering non necessari.

Utilizzo avanzato di useEffect: ripulire gli effetti collaterali

L’hook useEffect permette di eseguire effetti collaterali e fornisce un meccanismo per ripulire questi effetti collaterali. In questo modo fa sì che le risorse o le sottoscrizioni create durante l’effetto collaterale vengano rilasciate correttamente e si evitino perdite di memoria.

Vediamo come ripulire gli effetti collaterali con l’hook useEffect:

useEffect(() => {
  // Esegue qualche effetto collaterale

  // Pulisce l'effetto collaterale
  return () => {
	// Pulisce i task
  };
}, []);

Nello snippet di codice qui sopra, la funzione di pulizia è definita come valore di ritorno all’interno dell’Hook useEffect. Questa funzione viene invocata quando il componente sta per essere smontato o prima che avvenga un nuovo rendering. Permette di ripulire qualsiasi risorsa o sottoscrizione stabilita durante l’effetto collaterale.

Ecco alcuni esempi avanzati dell’hook useEffect per la pulizia degli effetti collaterali:

1. Cancellazione degli Intervalli

useEffect(() => {
	const interval = setInterval(() => {
    	// Esegue qualche azione ripetuta
	}, 1000);
	return () => {
    	clearInterval(interval); // Pulisce l'intervallo
	};
}, []);

In questo esempio, abbiamo impostato un intervallo che esegue un’azione ogni secondo. La funzione di pulizia cancella l’intervallo per evitare che venga eseguito dopo che il componente è stato smontato.

2. Pulizia dei Listener di Eventi

useEffect(() => {
	const handleClick = () => {
    	// Gestisce l'evento click
	};

	window.addEventListener('click', handleClick);

	return () => {
    	window.removeEventListener('click', handleClick); // Pulisce l'event listener
	};
}, []);

Qui creiamo un listener di eventi per l’evento click sull’oggetto window. La funzione di pulizia rimuove il listener di eventi per evitare perdite di memoria e garantire una corretta pulizia.

Ricordate che la funzione di pulizia è facoltativa, ma è altamente consigliato ripulire tutte le risorse o le sottoscrizioni per mantenere sana ed efficiente un’applicazione.

Utilizzare l’hook useEffect

L’hook useEffect permette di eseguire operazioni che prevedono l’interazione con entità o API esterne, come API web come localStorage o fonti di dati esterne.

Vediamo come utilizzare l’hook useEffect in diversi scenari:

1. Lavorare con le API web (localStorage)

useEffect(() => {
 // Memorizzazione dei dati nel localStorage
  localStorage.setItem('key', 'value');
  // Recupero dei dati dal localStorage
  const data = localStorage.getItem('key');
  // Pulizia: Cancellazione del localStorage quando si smonta un componente
  return () => {
	localStorage.removeItem('key');
  };
}, []);

In questo esempio, l’hook useEffect è utilizzato per memorizzare e recuperare i dati dal localStorage del browser. La funzione di pulizia assicura che il localStorage venga cancellato quando il componente viene smontato (questo potrebbe non essere sempre un buon caso d’uso perché si potrebbe voler mantenere i dati del localStorage finché il browser non viene aggiornato).

2. Recuperare i dati da un’API esterna

useEffect(() => {
  // Recupera i dati da un'API esterna
  fetch('https://api.example.com/data')
	.then((response) => response.json())
	.then((data) => {
  	// Fa qualcosa con i dati
	});
}, []);

In questo caso, l’hook useEffect viene utilizzato per recuperare i dati da un’API esterna. I dati recuperati possono poi essere elaborati e utilizzati all’interno del componente. Non è obbligatorio aggiungere sempre una funzione di pulizia.

Altri effetti collaterali

L’hook useEffect può essere utilizzato per diversi altri effetti collaterali, come ad esempio:

A. Iscrizione a eventi:

useEffect(() => {
  window.addEventListener('scroll', handleScroll);
  return () => {
	window.removeEventListener('scroll', handleScroll);
  };
}, []);

B. Modifica del titolo del documento:

useEffect(() => {
  document.title = 'New Title';
  return () => {
	document.title = 'Previous Title';
  };
}, []);

C. Gestione di timer:

useEffect(() => {
  const timer = setInterval(() => {
	// Fa qualcosa ripetutamente
  }, 1000);
  return () => {
	clearInterval(timer);
  };
}, []);

Errori comuni di useEffect e come evitarli

Lavorando con useEffect in React, è possibile imbattersi in errori che possono portare a comportamenti inaspettati o a problemi di prestazioni.

Comprendere questi errori e sapere come evitarli può aiutare a migliorare il proprio codice.

Analizziamo alcuni errori comuni di useEffect e le rispettive soluzioni:

1. Array di dipendenze mancante

Un errore comune è quello di dimenticare di includere un array di dipendenze come secondo argomento del comando useEffect.

ESLint segnala sempre questo errore come un avvertimento perché può causare comportamenti indesiderati, come un eccessivo re-rendering o dati non aggiornati.

useEffect(() => {
  // Codice dell'effetto collaterale
}); // Array di dipendenze assente

Soluzione: fornire sempre a useEffect un array di dipendenze, anche se vuoto. Includere tutte le variabili o i valori da cui dipende l’effetto. Questo aiuta React a stabilire quando l’effetto deve essere eseguito o saltato.

useEffect(() => {
  // Codice dell'effetto collaterale
}, []); // Array di dipendenze vuoto o con dipendenze appropriate

2. Array di dipendenze non corretto

Anche un array di dipendenze non corretto può causare dei problemi. Se l’array di dipendenze non è definito con precisione, l’effetto potrebbe non funzionare quando le dipendenze previste cambiano.

const count = 5;
const counter = 0;
useEffect(() => {
  // Codice dell'effetto collaterale che dipende da 'count'
  let answer = count + 15;
}, [count]); // Array di dipendenze non corretto

Soluzione: assicurarsi di includere tutte le dipendenze necessarie nell’array di dipendenze. Se l’effetto dipende da più variabili, includerle tutte per attivare l’effetto quando una qualsiasi delle dipendenze cambia.

const count = 5;
useEffect(() => {
  // Codice dell'effetto collaterale che dipende da 'count'
  let answer = count + 15;
}, [count]); // Array di dipendenze corretto

3. Loop infiniti

Quando l’effetto modifica uno stato o un oggetto che dipende dall’effetto stesso, può generarsi un loop infinito. Ciò comporta che l’effetto venga attivato ripetutamente, causando un eccessivo re-rendering e potenzialmente il blocco dell’applicazione.

const [count, setCount] = useState(0);
useEffect(() => {
  setCount(count + 1); // Modifica della dipendenza 'count' all'interno dell'effetto
}, [count]); // L'array delle dipendenze include 'count'

Soluzione: Assicurarsi che l’effetto non modifichi direttamente una dipendenza inclusa nel suo array di dipendenze. Al contrario, creare variabili separate o utilizzare altre tecniche di gestione dello stato per gestire le modifiche necessarie.

const [count, setCount] = useState(0);
useEffect(() => {
  setCount((prevCount) => prevCount + 1); // Modificare "count" utilizzando una callback
}, []); // Si può tranquillamente rimuovere la dipendenza da 'count'

4. Trascurare la pulizia

Trascurare la pulizia degli effetti collaterali può portare a perdite di memoria o a un inutile consumo di risorse. Non ripulire i listener di eventi, gli intervalli o le sottoscrizioni può causare un comportamento inatteso, soprattutto quando il componente si smonta.

useEffect(() => {
  const timer = setInterval(() => {
	// Esegue ripetutamente un'azione
  }, 1000);
  // Pulizia mancante
  return () => {
	clearInterval(timer); // Pulizia mancante nell'istruzione return
  };
}, []);

Soluzione: Fornire sempre una funzione di pulizia nella dichiarazione di ritorno dell’hook useEffect.

useEffect(() => {
  const timer = setInterval(() => {
	// Esegue ripetutamente un'azione
  }, 1000);
  return () => {
	clearInterval(timer); // Pulizia inclusa nella dichiarazione di ritorno
  };
}, []);

Conoscendo questi errori di useEffect e seguendo le soluzioni consigliate, sarà possibile evitare possibili insidie e garantire un corretto ed efficiente utilizzo dell’hook useEffect.

Riepilogo

L’hook useEffect di React è un potente strumento per gestire gli effetti collaterali nei componenti funzionali. Con una conoscenza più approfondita di useEffect, potrete dare vita ad applicazioni React più efficienti.

E potete anche provare dal vivo la vostra applicazione React distribuendola gratuitamente sull’Hosting di Applicazioni di Kinsta!

Ora tocca a voi. Cosa pensate dell’hook useEffect? Scrivetelo nella sezione dei commenti qui sotto.