Gli hook in React sono funzioni che ti permettono di "agganciarti" alle funzionalità di React, come lo stato e il ciclo di vita dei componenti, da componenti funzionali. Prima dell'introduzione degli hook con React 16.8, queste funzionalità erano disponibili solo nei componenti di classe. Gli hook più comunemente usati sono useState
, useEffect
, useContext
, useReducer
, e useRef
.
useState
L'hook useState
è uno degli hook fondamentali in React per gestire lo stato locale di un componente funzionale. Prima dell'introduzione degli hook, lo stato locale poteva essere usato solo nei componenti di classe. useState
rende possibile utilizzare lo stato anche nei componenti funzionali, rendendoli più potenti e flessibili.
Sintassi Base di useState
La sintassi base di useState
è piuttosto semplice:
1const [state, setState] = useState(initialState);
initialState
: il valore iniziale dello stato. Può essere qualsiasi tipo di dato: numero, stringa, array, oggetto, ecc.state
: la variabile corrente dello stato, inizializzata coninitialState
.setState
: la funzione che viene utilizzata per aggiornarestate
. QuandosetState
viene chiamata, React ri-renderizza il componente con il nuovo stato.
Esempio di utilizzo di useState
Ecco un esempio dettagliato di come utilizzare useState
in un componente funzionale React con TypeScript:
1import React, { useState } from 'react';
2
3const Counter: React.FC = () => {
4 // Inizializza lo stato 'count' con valore 0
5 const [count, setCount] = useState<number>(0);
6
7 const handleIncrement = () => {
8 // Aggiorna lo stato 'count', incrementandolo di 1
9 setCount(prevCount => prevCount + 1);
10 };
11
12 const handleDecrement = () => {
13 // Aggiorna lo stato 'count', decrementandolo di 1
14 setCount(prevCount => prevCount - 1);
15 };
16
17 return (
18 <div>
19 <p>Conto attuale: {count}</p>
20 <button onClick={handleIncrement}>Incrementa</button>
21 <button onClick={handleDecrement}>Decrementa</button>
22 </div>
23 );
24};
Punti Chiave di useState
- Immutabilità dello Stato: È importante ricordare che lo stato in React è immutabile. Questo significa che non dovresti mai modificare direttamente lo stato (ad esempio, usando
state++
). Invece, dovresti sempre utilizzare la funzionesetState
per aggiornare lo stato, che crea una nuova istanza dello stato anziché modificarlo direttamente. - Funzioni di Aggiornamento Lazy: Quando il nuovo stato dipende dallo stato precedente, è consigliato usare una funzione di aggiornamento che riceve lo stato precedente come argomento, garantendo che l'aggiornamento sia basato sul valore attuale dello stato.
- Valori Iniziali Calcolati: Se il valore iniziale dello stato ha bisogno di qualche calcolo, è possibile passare una funzione a
useState
. Questa funzione verrà eseguita solo al momento della creazione del componente, migliorando le performance se il calcolo iniziale è costoso.
1const [state, setState] = useState(() => {
2 // Calcola il valore iniziale complesso qui
3 const initialState = someExpensiveComputation(props);
4 return initialState;
5});
useState in breve
useState
è uno strumento potente che introduce la gestione dello stato nei componenti funzionali di React, consentendo di costruire applicazioni interattive e reattive con meno boilerplate rispetto ai componenti di classe. La sua semplicità d'uso, unita alla potenza di React, rende la creazione di interfacce utente dinamiche e interattive molto più accessibile e gestibile.
useEffect
L'hook useEffect
è un altro strumento fondamentale nell'arsenale di React che permette di eseguire effetti collaterali in componenti funzionali. Questo hook è il sostituto dei metodi di ciclo di vita componentDidMount
, componentDidUpdate
, e componentWillUnmount
nei componenti di classe, consolidandoli in un'unica API.
Sintassi Base di useEffect
La sintassi base di useEffect
è la seguente:
1useEffect(() => {
2 // Codice per l'effetto collaterale
3
4 return () => {
5 // Pulizia (opzionale)
6 };
7}, [dipendenze]);
- Il primo argomento è una funzione che React chiamerà dopo che il DOM è stato aggiornato. Questa funzione può eseguire qualsiasi effetto collaterale, come operazioni di input/output, sottoscrizioni, o manualmente modificare il DOM.
- Il secondo argomento è un array di dipendenze che determina quando l'effetto dovrebbe essere ri-eseguito. Se l'array è vuoto (
[]
), l'effetto viene eseguito solo al montaggio e al smontaggio del componente. Se l'array contiene valori, l'effetto verrà ri-eseguito solo quando i valori specificati cambiano.
Esempio di utilizzo di useEffect
Ecco come useEffect
può essere utilizzato in un componente funzionale React con TypeScript:
1import React, { useState, useEffect } from 'react';
2
3const Timer: React.FC = () => {
4 const [seconds, setSeconds] = useState(0);
5
6 useEffect(() => {
7 // Imposta un timer che aggiorna 'seconds' ogni secondo
8 const interval = setInterval(() => {
9 setSeconds(prevSeconds => prevSeconds + 1);
10 }, 1000);
11
12 // Funzione di pulizia che viene chiamata al smontaggio del componente
13 return () => clearInterval(interval);
14 }, []); // L'array vuoto indica che questo effetto non ha dipendenze e viene eseguito solo al montaggio
15
16 return <div>Secondi trascorsi: {seconds}</div>;
17};
Punti Chiave di useEffect
- Effetti Collaterali: Gli effetti collaterali sono operazioni che possono influenzare componenti esterni al componente che viene renderizzato, come richieste di rete, sottoscrizioni, o manualmente modificare il DOM.
useEffect
gestisce questi effetti all'interno dei componenti funzionali. - Pulizia dell'Effetto: Se l'effetto collaterale necessita di pulizia (ad esempio, cancellare un timer o annullare una sottoscrizione), puoi restituire una funzione di pulizia dall'interno della funzione passata a
useEffect
. React chiamerà questa funzione di pulizia quando il componente viene smontato o prima di eseguire l'effetto la prossima volta, garantendo che le risorse siano liberate correttamente. - Ottimizzazione delle Prestazioni: Utilizzando l'array di dipendenze, puoi controllare quanto frequentemente l'effetto viene eseguito, evitando esecuzioni inutili e migliorando le prestazioni dell'applicazione.
useEffect in breve
useEffect
offre un meccanismo potente e flessibile per eseguire effetti collaterali nei componenti funzionali di React. Attraverso la sua API unificata, rende più semplice gestire le operazioni laterali, come sottoscrizioni a eventi esterni o timer, all'interno dei componenti, mantenendo il codice organizzato e performante.
useContext
L'hook useContext
di React consente di accedere ai valori di un contesto (context
) senza dover utilizzare il componente Consumer
all'interno di un componente funzionale. Questo rende molto più semplice e pulito l'accesso ai dati condivisi come temi, preferenze linguistiche, dati utente, ecc., attraverso l'albero dei componenti.
Sintassi Base di useContext
La sintassi base di useContext
è la seguente:
1const value = useContext(MyContext);
MyContext
: il contesto dal quale si desidera leggere il valore corrente. Questo contesto (context
) deve essere creato utilizzandoReact.createContext
e può essere distribuito a tutti i componenti dell'applicazione tramite un componenteProvider
.
Esempio di utilizzo di useContext
Ecco un esempio pratico di come utilizzare useContext
in un componente funzionale React con TypeScript:
1import React, { useContext, createContext } from 'react';
2
3// Creazione di un contesto con un valore predefinito
4const ThemeContext = createContext('light');
5
6const ThemedButton: React.FC = () => {
7 // Utilizzo di useContext per accedere al valore corrente del contesto
8 const theme = useContext(ThemeContext);
9
10 return <button className={theme}>Sono un bottone a tema {theme}</button>;
11};
12
13const App: React.FC = () => {
14 return (
15 // Il Provider rende il valore "dark" disponibile a tutti i componenti discendenti
16 <ThemeContext.Provider value="dark">
17 <ThemedButton />
18 </ThemeContext.Provider>
19 );
20};
Se avessimo deciso di non optare per useContext
, ecco un esempio di come potresti passare il tema ai componenti discendenti senza utilizzare l'hook, ma tramite il passaggio esplicito delle props. Questo approccio dimostra come si lavorava prima dell'introduzione degli hook o in scenari in cui si preferisce evitare l'uso del contesto per motivi di semplicità o di architettura del progetto.
1import React from 'react';
2
3// Componente Button che riceve il tema come prop
4const ThemedButton: React.FC<{ theme: string }> = ({ theme }) => {
5 return <button className={theme}>Sono un bottone a tema {theme}</button>;
6};
7
8// Componente App che passa il tema esplicitamente a ThemedButton
9const App: React.FC = () => {
10 const theme = 'dark'; // Definizione del tema all'interno del componente App
11 return (
12 <div>
13 {/* Passaggio del tema come prop a ThemedButton */}
14 <ThemedButton theme={theme} />
15 </div>
16 );
17};
Nello specifico cosa è stato fatto:
- Definizione del Tema: In questo esempio, il tema è definito come una variabile all'interno del componente
App
. Questo tema è poi passato direttamente al componenteThemedButton
come una prop. - Passaggio delle Props: Il componente
ThemedButton
riceve il tema come una prop e lo utilizza per determinare la classe del bottone. Questo metodo richiede che ogni componente che necessita del tema debba riceverlo esplicitamente tramite le props da ogni componente genitore. - Manutenibilità: Mentre questo approccio funziona bene per strutture di componenti piccole o per dati che cambiano raramente, può diventare rapidamente oneroso e difficile da mantenere man mano che l'applicazione cresce e il numero di componenti che necessitano del dato condiviso aumenta.
Le differenze principali con l’esempio con useContext:
- Verbosità: Senza
useContext
, il dato (in questo caso, il tema) deve essere passato esplicitamente attraverso l'albero dei componenti, da genitore a figlio. Questo può aumentare la verbosità e complicare la manutenzione, specialmente in applicazioni di grandi dimensioni. - Efficienza: L'uso di
useContext
riduce il boilerplate necessario per passare dati attraverso l'albero dei componenti, rendendo il codice più pulito e facile da seguire. - Riutilizzabilità: Con
useContext
, i componenti possono accedere ai dati del contesto direttamente, senza dipendere dalle props passate dai loro genitori, migliorando la riutilizzabilità dei componenti.
L'introduzione di useContext
in React ha significativamente semplificato la condivisione dei dati tra componenti, specialmente in applicazioni complesse, rendendo l'architettura dell'applicazione più pulita e meno propensa ad errori di propagazione dei dati.
Punti Chiave di useContext
- Accesso Semplice ai Dati Condivisi:
useContext
rende molto più semplice per i componenti discendenti accedere ai dati condivisi senza doverli passare esplicitamente attraverso ogni livello dell'albero dei componenti. - Riduce il Boilerplate: A differenza dell'approccio classico che utilizza il componente
Consumer
per accedere ai dati del contesto,useContext
permette di accedere direttamente al valore del contesto con una singola riga di codice, riducendo significativamente il boilerplate. - Uso con Provider: Per poter utilizzare
useContext
, il componente che desidera accedere ai dati del contesto deve trovarsi all'interno dell'albero dei componenti di unProvider
per quel contesto specifico. IlProvider
permette di sovrascrivere il valore del contesto per tutti i suoi componenti discendenti. - Performance: L'uso di
useContext
non introduce di per sé problemi di performance significativi, ma è importante essere consapevoli che ogni aggiornamento al contesto causerà il re-render dei componenti consumatori. Pertanto, è buona pratica ottimizzare i componenti consumatori per evitare render inutili.
useContext in breve
useContext
è uno strumento essenziale per la gestione efficace dei dati condivisi all'interno delle applicazioni React. Facilita notevolmente l'accesso ai valori dei contesti senza la necessità di passaggi di props multi-livello, mantenendo il codice pulito e organizzato. L'uso di useContext
in combinazione con un'architettura ben pensata dell'applicazione può significativamente migliorare l'efficienza dello sviluppo e la manutenibilità del codice.
useReducer
L'hook useReducer
è un altro strumento essenziale in React per la gestione dello stato, in particolare quando si ha a che fare con logiche di aggiornamento dello stato più complesse. useReducer
offre un'alternativa a useState
, permettendo di gestire lo stato attraverso reducer - funzioni che determinano come cambia lo stato in risposta ad azioni.
Sintassi Base di useReducer
La sintassi base di useReducer
è la seguente:
1const [state, dispatch] = useReducer(reducer, initialState);
reducer
: Una funzione che riceve lo stato corrente e un'azione, e restituisce un nuovo stato. La forma della funzione reducer è(state, action) => newState
.initialState
: Il valore iniziale dello stato.state
: Lo stato corrente, tenuto traccia dauseReducer
.dispatch
: Una funzione che puoi chiamare con un'azione per richiedere un aggiornamento dello stato. L'azione viene poi gestita dal tuo reducer per determinare il nuovo stato.
Esempio di utilizzo di useReducer
Supponiamo di avere un componente che gestisce un contatore. Utilizzeremo useReducer
per gestire lo stato del contatore.
1import React, { useReducer } from 'react';
2
3// Definizione del tipo di azione
4type ActionType = { type: 'increment' } | { type: 'decrement' };
5
6// Funzione reducer che determina come cambia lo stato in base all'azione ricevuta
7function counterReducer(state: number, action: ActionType): number {
8 switch (action.type) {
9 case 'increment':
10 return state + 1;
11 case 'decrement':
12 return state - 1;
13 default:
14 throw new Error();
15 }
16}
17
18const Counter: React.FC = () => {
19 // Uso di useReducer per gestire lo stato del contatore
20 const [count, dispatch] = useReducer(counterReducer, 0); // Inizializzazione a 0
21
22 return (
23 <div>
24 Conto: {count}
25 <button onClick={() => dispatch({ type: 'increment' })}>+</button>
26 <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
27 </div>
28 );
29};
Punti Chiave di useReducer
- Gestione dello Stato Complesso:
useReducer
è particolarmente utile quando lo stato logico del componente diventa complesso o quando il prossimo stato dipende dal precedente in modi non banali. - Performance:
useReducer
può anche ottimizzare le prestazioni per componenti che attivano aggiornamenti profondi, poiché è possibile passaredispatch
verso il basso invece di callback, evitando così la creazione di nuove funzioni a ogni render. - Leggibilità e Manutenibilità: Separare la logica di aggiornamento dello stato dal componente può rendere il codice più leggibile e più facile da mantenere, specialmente per logiche di stato complesse.
- Uso con Context:
useReducer
è spesso usato in combinazione conuseContext
per gestire e distribuire lo stato attraverso componenti profondamente annidati, offrendo un'alternativa più leggera e flessibile alla Redux per la gestione dello stato globale.
useReducer in breve
useReducer
fornisce un metodo potente e flessibile per gestire lo stato all'interno dei componenti funzionali React, specialmente quando ci si trova di fronte a logiche di aggiornamento complesse. Attraverso l'uso di reducer per gestire come lo stato cambia in risposta ad azioni, i componenti possono rimanere organizzati, testabili e manutenibili.
useRef
L'hook useRef
è uno strumento versatile in React che può essere utilizzato per diverse finalità, tra cui l'accesso diretto agli elementi del DOM e la conservazione di un valore mutabile attraverso i render del componente senza causare un aggiornamento dello stesso. A differenza dello stato gestito tramite useState
o useReducer
, le modifiche a un ref non causano il re-render del componente.
Sintassi Base di useRef
La sintassi base di useRef
è la seguente:
1const refContainer = useRef(initialValue);
initialValue
: Il valore iniziale del ref. Il valore contenuto nel ref può essere qualsiasi tipo di dato.refContainer
: Un oggetto contenente una proprietà.current
che viene inizializzata al valore passato aduseRef
.
Esempio di utilizzo di useRef
Ecco un esempio pratico che mostra come utilizzare useRef
per accedere a un elemento del DOM in un componente funzionale React con TypeScript.
1import React, { useRef, useEffect } from 'react';
2
3const TextInputWithFocus: React.FC = () => {
4 // Crea un ref per l'elemento input del DOM
5 const inputEl = useRef<HTMLInputElement>(null);
6
7 useEffect(() => {
8 // Mette automaticamente il focus sull'input dopo il montaggio del componente
9 inputEl.current?.focus();
10 }, []);
11
12 return <input ref={inputEl} type="text" />;
13};
Punti Chiave di useRef
- Accesso agli Elementi del DOM:
useRef
fornisce un modo per accedere direttamente agli elementi del DOM in React. Questo è utile per manipolare l'elemento, ad esempio, per impostare il focus o per leggere il valore. - Valori Mutabili che non Causano Re-render: A differenza di
useState
, l'aggiornamento del valore contenuto in.current
di un ref non provoca il re-render del componente. Questo lo rende ideale per tenere traccia di valori che devono persistere tra i render ma non necessitano di causare un aggiornamento dell'interfaccia utente. - Persistenza dei Dati tra i Render: I refs possono essere utilizzati per conservare qualsiasi valore mutabile che vuoi persistere esattamente come è tra i render, senza causare l'aggiornamento del componente ogni volta che il valore cambia.
Dimostrare l’assenza di re-rendering in useRef
Per dimostrare che useState
causa il re-render di un componente in React mentre useRef
no, possiamo creare un semplice esperimento con due componenti: uno che utilizza useState
e l'altro che utilizza useRef
. In entrambi i componenti, incrementeremo il valore memorizzato (stato o ref) ogni volta che l'utente clicca un bottone. Aggiungeremo anche un messaggio di log nella console per osservare quando avviene il re-render del componente.
Componente con useState
Questo componente dimostra che l'aggiornamento dello stato tramite useState
causa il re-render del componente.
1import React, { useState } from 'react';
2
3const StateCounter: React.FC = () => {
4 const [count, setCount] = useState(0);
5
6 console.log('StateCounter render');
7
8 return (
9 <div>
10 <p>Conto (useState): {count}</p>
11 <button onClick={() => setCount(count + 1)}>Incrementa</button>
12 </div>
13 );
14};
Ogni volta che clicchi il bottone per incrementare il conto, vedrai il messaggio "StateCounter render" nella console, indicando che il componente è stato re-renderizzato.
Componente con useRef
Questo componente dimostra che l'aggiornamento di un ref tramite useRef
non causa il re-render del componente.
1import React, { useRef } from 'react';
2
3const RefCounter: React.FC = () => {
4 const count = useRef(0);
5
6 console.log('RefCounter render');
7
8 return (
9 <div>
10 <p>Conto (useRef): {count.current}</p>
11 <button onClick={() => count.current += 1}>Incrementa</button>
12 </div>
13 );
14};
In questo caso, anche se clicchi il bottone per incrementare il conto, il valore visualizzato non cambierà e il messaggio "RefCounter render" apparirà nella console solo una volta al montaggio del componente. Questo perché l'aggiornamento di .current
su un ref non causa il re-render del componente.
Dimostrazione in App
Per visualizzare entrambi i componenti e testare il comportamento, puoi inserirli in un'applicazione React principale:
1import React from 'react';
2import StateCounter from './StateCounter'; // Assumi che StateCounter sia definito in StateCounter.tsx
3import RefCounter from './RefCounter'; // Assumi che RefCounter sia definito in RefCounter.tsx
4
5const App: React.FC = () => {
6 return (
7 <div>
8 <StateCounter />
9 <RefCounter />
10 </div>
11 );
12};
13
14export default App;
Questo esperimento dimostra chiaramente che l'aggiornamento dello stato con useState
causa il re-render di un componente in React, mentre l'aggiornamento di un valore memorizzato in un useRef
non lo fa. useState
è usato per dati che cambiano e dovrebbero causare un aggiornamento dell'interfaccia, mentre useRef
è utile per mantenere una referenza mutabile attraverso i render senza influenzare il ciclo di re-render del componente.
useRef in breve
useRef
è uno strumento essenziale in React per gestire riferimenti diretti agli elementi del DOM e per conservare dati mutabili attraverso i render senza causare aggiornamenti del componente. La sua capacità di fornire un punto di accesso persistente ai dati lo rende unico rispetto ad altri hook di React e apre varie possibilità per la manipolazione del DOM e la gestione dei dati all'interno dei componenti funzionali.
Caricamento...
Diventiamo amici di penna? ✒️
Iscriviti alla newsletter per ricevere una mail ogni paio di settimane con le ultime novità, gli ultimi approfondimenti e gli ultimi corsi gratuiti puubblicati. Ogni tanto potrei scrivere qualcosa di commerciale, ma solo se mi autorizzi, altrimenti non ti disturberò oltre.
Se non ti piace la newsletter ti ricordo la community su Discord, dove puoi chiedere aiuto, fare domande e condividere le tue esperienze (ma soprattutto scambiare due meme con me). Ti aspetto!