
GUIDE PER ASPIRANTI PROGRAMMATORI
Gli effetti collaterali in React
Abbiamo detto che le funzioni pure ci regalano riutilizzabilità, componibilità e confidenza, ma non possiamo rinunciare agli effetti collaterali: avremo sempre bisogno di salvare dati, mandare richieste di rete, modificare strutture di dati. React prevede due momenti in cui possiamo eseguire effetti collaterali, uno per React e uno per noi: Il rendering: l’attività con cui…


Vuoi avviare una nuova carriera o fare un upgrade?
Trova il corso Digital & Tech più adatto a te nel nostro catalogo!
Abbiamo detto che le funzioni pure ci regalano riutilizzabilità, componibilità e confidenza, ma non possiamo rinunciare agli effetti collaterali: avremo sempre bisogno di salvare dati, mandare richieste di rete, modificare strutture di dati.
React prevede due momenti in cui possiamo eseguire effetti collaterali, uno per React e uno per noi:
- Il rendering: l’attività con cui React modifica il DOM (gli elementi HTML) per visualizzare il nostro codice JSX, è un effetto collaterale. Quello che fanno i nostri componenti è trasformare props e/o stati in JSX, dopodiché, alla fine di ogni ciclo di rendering, React applica l’effetto collaterale di inserire gli elementi derivati da JSX nella pagina HTML.
- Le funzioni hook: le funzioni setter restituite da useState rappresentano effetti collaterali. I nostri hook useQuery e useCommand rappresentano effetti collaterali, per cui, oltre a trasformare le props e gli stati in JSX, lanciamo delle chiamate di rete.
Gli effetti collaterali – che in programmazione imperativa possono essere anche il 100% del codice – sono i più difficili da individuare per le persone non abituate alla programmazione dichiarativa. Per fortuna, in React, essendo i componenti stessi funzioni, è più facile rispondere alle domande “il mio componente fa qualcos’altro oltre a trasformare le props e gli stati in JSX? Se sì, ho isolato gli effetti collaterali dentro funzioni hook?”.
Ora che sappiamo cosa sono gli effetti collaterali, possiamo vedere il prossimo hook incluso in React, creato apposta per eseguire effetti collaterali: useEffect.
useEffect in React
Come abbiamo appena accennato, useEffect è la funzione hook che React ci fornisce per applicare effetti collaterali all’interno dei nostri componenti. Questa è la sintassi generica di useEffect:
useEffect( () => { // side effect // optional return () => { // cleanup }; }, [ /* dependencies */ ] );
useEffect è una funzione che accetta due argomenti:
- La funzione che rappresenta l’effetto collaterale. Questa funzione non può avere un valore di ritorno (in altre parole, deve restituire void). Gli effetti collaterali non restituiscono mai un valore perché, per definizione, fanno qualcosa di slegato dal resto del sistema. La funzione che passiamo come primo argomento di useEffect può restituire una seconda funzione, che possiamo chiamare cleanup function (in italiano, funzione di pulizia). La cleanup function è una funzione che rimuove eventuali effetti collaterali persistenti. Per esempio:
useEffect(() => { const onWindowResize = () => { console.log("The window has been resized!"); }; window.addEventListener("resize", onWindowResize); return () => { window.removeEventListener("resize", onWindowResize); }; }, []);
In questo esempio, ascoltiamo l’evento nativo del browser che scatta quando la finestra viene ridimensionata. La nostra cleanup function smette di ascoltare. Non siamo costretti a restituire una cleanup function, possiamo anche non restituire niente. Quando applichiamo un effetto permanente, però, per esempio, appunto, ascoltare un evento, restituiamo una cleanup function, altrimenti continueremmo ad ascoltare l’evento anche dopo che il componente è stato rimosso dalla pagina, creando errori e sprecando risorse del browser.
- Un array di dipendenze. Le dipendenze sono le props o gli stati che devono scatenare l’effetto collaterale in caso di aggiornamento. Per esempio:
export default function Profile({ authStatus }) { useEffect(() => { authStatus.match({ whenAnonymous: () => { window.localStorage.removeItem("auth"); }, whenLoggedIn: (credentials) => { window.localStorage.setItem( "auth", JSON.stringify(authStatus.credentials) ); }, }); }, [authStatus]); }
In questo esempio, immaginiamo di avere uno stato, da qualche parte in un componente controller, che rappresenta lo stato di autenticazione dell’utente. authStatus fornisce una funzione di matching che ha due stati: “anonimo” (“anonymous“) e “autenticato” (“logged_in“).
Quando l’utente è autenticato, la funzione di matching di authStatus fornisce la proprietà credentials, che contiene le credenziali di autenticazione dell’utente, come il token di autenticazione (mai salvare la password da nessuna parte!).
Lo stato di autenticazione viene passato al nostro componente view Profile, che salva le credenziali nell’archivio locale del browser se l’utente è autenticato, altrimenti (se è anonimo) le elimina. In questo caso, il nostro effetto collaterale deve essere eseguito ogni volta che lo stato di autenticazione viene aggiornato, così che possiamo reagire a login e logout eseguendo le operazioni corrette.
Aggiungiamo, quindi, authStatus all’array delle dipendenze dell’effetto, così che React esegua la funzione ogni volta che viene chiamata la funzione setter dello stato che si trova nel componente controller, e di conseguenza la prop viene aggiornata.
Attenzione: anche in questo caso, come per i cicli di rendering, non è esatto dire che gli effetti vengono eseguiti quando le cose cambiano. Gli effetti vengono eseguiti ogni volta che viene chiamata la funzione setter dello stato corrispondente alla nostra dipendenza, anche se il prossimo valore dello stato è identico a quello corrente. Tutti gli effetti, a prescindere dalle loro dipendenze, vengono anche eseguiti quando il componente che li contiene viene creato.
Le dipendenze degli effetti in React
La regola di React per le dipendenze degli effetti è questa: tutte le variabili che vengono citate all’interno della funzione che rappresenta l’effetto (quella che passiamo come primo argomento a useEffect) devono essere inserite tra le dipendenze, tranne le funzioni setter degli stati.
Attenzione: se una prop è, in realtà, una funzione setter passata dal componente controller, non vale come funzione setter, va comunque inclusa tra le dipendenze. Le uniche funzioni setter che si possono omettere sono quelle che fanno riferimento a stati nello stesso componente in cui usiamo useEffect.
React, attraverso ESLint, ci darà un avvertimento se una delle variabili citate all’interno della funzione che rappresenta l’effetto non fa parte delle dipendenze, o se ne abbiamo messe di troppo. Ci dirà anche che possiamo aggiungere un commento nella riga di codice precedente a quella in cui abbiamo dichiarato le dipendenze, per spegnere l’avvertimento: non farlo mai. Nel 99% dei casi, se una variabile citata all’interno della funzione che rappresenta l’effetto non fa parte delle dipendenze, significa che c’è un errore di logica nel modo in cui stiamo progettando il sistema.
Il ciclo di rendering di React e gli effetti collaterali
Sappiamo come funziona il ciclo di rendering di React quando abbiamo a che fare con props, stati ed eventi:
- Al primo ciclo di rendering, tutte le funzioni che rappresentano i componenti in pagina vengono eseguite con i loro stati iniziali.
- Quando un evento scatena la chiamata di una funzione setter, tutti i componenti che usano lo stato che è cambiato, e tutti i loro figli, vengono chiamati al rendering con il nuovo valore dello stato.
Ma come si comporta React con gli effetti collaterali? Gli effetti collaterali vengono eseguiti dopo il ciclo di rendering:
- Tutti gli effetti collaterali che dipendono dallo stato che è cambiato vengono eseguiti.
Nella prossima sezione, analizzeremo una serie di casi in cui l’uso di effetti collaterali è sconsigliato, e il fatto che scatenino cicli di rendering è uno dei motivi per cui lo è.
CONTENUTI GRATUITI IN EVIDENZA
Guide per aspiranti programmatori 👨🏻🚀
Vuoi muovere i primi passi nel Digital e Tech? Abbiamo preparato alcune guide per aiutarti a orientarti negli ambiti più richiesti oggi.