Guida Completa a useState in React: Gestire lo Stato per Principianti

Principiante
JavaScript React

Impara a utilizzare l'hook useState di React per creare componenti dinamici e interattivi. Una guida passo-passo per chi inizia con React.

Pubblicato
Tag
Web Development javascript frontend React Hooks useState

Introduzione a useState e al concetto di Stato

Se stai iniziando il tuo viaggio con React, una delle prime e più importanti scoperte che farai è che React non è semplicemente un modo per renderizzare HTML, ma un modo per gestire i dati che cambiano nel tempo. In termini tecnici, questo "dato che cambia" viene chiamato Stato (State).

Immagina una pagina web statica: una volta caricata, il testo rimane lo stesso finché non ricarichi la pagina. Ora immagina un'applicazione moderna, come Facebook o Instagram: quando clicchi su un tasto "Like", il cuore diventa rosso e il numero dei like aumenta istantaneamente. Questo accade perché lo stato dell'interfaccia è cambiato, e React ha reagito a questo cambiamento aggiornando solo la parte di pagina necessaria.

L'hook useState è lo strumento fondamentale che React mette a disposizione per permettere ai componenti funzionali di "ricordare" informazioni tra un render e l'altro. Senza useState, ogni volta che un componente viene ricaricato, tutte le variabili locali verrebbero resettate ai loro valori iniziali, rendendo impossibile creare interazioni dinamiche.

Come funziona useState: La sintassi

useState è una funzione speciale chiamata Hook. Gli Hook sono funzioni che permettono di "agganciarsi" alle funzionalità di React all'interno di componenti funzionali (che in passato erano chiamati componenti senza stato).

Per utilizzare useState, devi prima importarlo dal pacchetto React:

import React, { useState } from 'react';

La sintassi tipica di useState utilizza la cosiddetta destrutturazione dell'array di JavaScript. Ecco come appare una dichiarazione standard:

const [state, setState] = useState(initialValue);

Analisi dei componenti della sintassi

  1. state (La variabile di stato): È il valore attuale dello stato. Puoi usarlo all'interno del tuo JSX per visualizzare il dato (ad esempio, {count}).
  2. setState (La funzione di aggiornamento): È l'unica funzione che devi usare per modificare il valore di state. Non puoi mai modificare state direttamente (es. state = 5 non funzionerà), perché React non saprebbe che deve ricaricare il componente.
  3. initialValue (Il valore iniziale): È il valore che lo stato avrà al primo render. Può essere qualsiasi cosa: un numero, una stringa, un booleano, un array o un oggetto.

Il ciclo di vita: Perché non possiamo usare variabili normali?

Una domanda comune per i principianti è: "Perché non posso semplicemente dichiarare let count = 0; e poi fare count++?"

La risposta risiede nel modo in cui React gestisce l'interfaccia utente. React utilizza un concetto chiamato Reconciliation. Quando lo stato di un componente cambia, React esegue nuovamente la funzione del componente (il render), calcola la differenza tra il vecchio HTML e il nuovo, e aggiorna solo i nodi del DOM che sono effettivamente cambiati.

Se usi una variabile normale (let), il valore potrebbe effettivamente cambiare in memoria, ma React non ha modo di sapere che quel cambiamento debba scatenare un nuovo render. Di conseguenza, l'utente vedrà ancora il vecchio valore sullo schermo. Usando la funzione di aggiornamento fornita da useState, comunichi esplicitamente a React: "Ehi, i dati sono cambiati, per favore aggiorna l'interfaccia!".

Esempi Pratici

Vediamo ora come applicare questi concetti in tre scenari comuni: un contatore, un campo di input e un toggle di visibilità.

Esempio 1: Il classico Contatore

Questo è l'esempio più semplice per capire il flusso: click $
ightarrow$ aggiornamento stato $
ightarrow$ nuovo render.

import React, { useState } from 'react';

function Counter() {
  // Dichiariamo lo stato 'count' con valore iniziale 0
  const [count, setCount] = useState(0);

  return (
    <div style={{ textAlign: 'center', marginTop: '20px' }}>
      <h2>Contatore: {count}</h2>
      <button => setCount(count + 1)}>Incrementa</button>
      <button => setCount(count - 1)}>Decrementa</button>
      <button => setCount(0)}>Reset</button>
    </div>
  );
}

export default Counter;

In questo esempio, ogni volta che premiamo un bottone, chiamiamo setCount. React aggiorna il valore di count e ridisegna il componente, mostrando il numero aggiornato.

Esempio 2: Gestione di un Input di Testo

In React, gli input sono spesso "controllati". Ciò significa che il valore dell'input è legato a uno stato di React.

import React, { useState } from 'react';

function Greeting() {
  const [name, setName] = useState('');

  const handleChange = (event) => {
    setName(event.target.value);
  };

  return (
    <div style={{ padding: '20px' }}>
      <input 
        type="text" 
        placeholder="Scrivi il tuo nome..." 
        value={name} 
      />
      <p>Ciao, {name ? name : 'Sconosciuto'}!</p>
    </div>
  );
}

export default Greeting;

Qui, l'evento onChange cattura ogni singola lettera digitata dall'utente e la salva nello stato name. Il componente si aggiorna in tempo reale, rendendo l'esperienza fluida.

Esempio 3: Gestione di Booleani (Toggle)

Molto utile per menu a tendina, modali o switch on/off.

import React, { useState } from 'react';

function ToggleText() {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <div style={{ padding: '20px' }}>
      <button => setIsVisible(!isVisible)}>
        {isVisible ? 'Nascondi' : 'Mostra'} Dettagli
      </button>
      {isVisible && <p>Ecco i dettagli segreti che erano nascosti!</p>}
    </div>
  );
}

export default ToggleText;

In questo caso, usiamo l'operatore di negazione ! per invertire il valore booleano: se è true diventa false e viceversa.

Gestione di stati complessi: Array e Oggetti

Fino a ora abbiamo usato numeri e stringhe. Ma cosa succede se dobbiamo gestire un oggetto o una lista? Qui è dove molti principianti commettono errori.

Il concetto di Immutabilità

In React, non devi mai modificare direttamente lo stato. Se hai un oggetto, non puoi fare user.name = 'Mario'. Perché? Perché React confronta il riferimento dell'oggetto vecchio con quello nuovo. Se modifichi una proprietà dell'oggetto ma il riferimento rimane lo stesso, React penserà che nulla sia cambiato e non aggiornerà la pagina.

Per aggiornare un oggetto o un array, devi creare una copia del dato esistente, modificarla e poi passarla alla funzione di aggiornamento. Per fare questo, usiamo l'operatore spread (...).

Esempio con Oggetto:

const [user, setUser] = useState({ name: 'Luca', age: 25 });

// Modo CORRETTO di aggiornare
setUser({ ...user, name: 'Marco' }); 
// Copiamo tutte le proprietà di user e sovrascriviamo solo name

Esempio con Array:

const [items, setItems] = useState(['Mela', 'Banana']);

// Modo CORRETTO di aggiungere un elemento
setItems([...items, 'Arancia']);
// Creiamo un nuovo array che contiene i vecchi elementi + l'arancia

Errori Comuni e Soluzioni

1. Aggiornamenti asincroni

Uno degli errori più frustranti è cercare di leggere lo stato immediatamente dopo averlo aggiornato:

setCount(count + 1);
console.log(count); // Stampa il valore VECCHIO, non quello nuovo!

setState non aggiorna la variabile istantaneamente, ma pianifica un aggiornamento per il prossimo render. Se hai bisogno di eseguire un'azione basata sul nuovo stato, dovresti usare l'hook useEffect o calcolare il nuovo valore in una variabile temporanea.

2. L'aggiornamento basato sullo stato precedente

Se l'aggiornamento dello stato dipende dal valore precedente (come in un contatore), il modo più sicuro è passare una funzione a setState invece di un valore:

setCount(prevCount => prevCount + 1);

Questo garantisce che tu stia lavorando con l'ultima versione dello stato, evitando bug rari ma fastidiosi legati alla natura asincrona di React.

3. Chiamare useState all'interno di cicli o condizioni

React richiede che gli Hook vengano chiamati sempre nello stesso ordine in ogni render. Non puoi mettere useState dentro un if o un for. Gli Hook devono stare sempre all'inizio della tua funzione componente.

FAQ Rapide

Q: Posso usare più useState in un unico componente?

Sì, assolutamente! È consigliabile separare lo stato in variabili diverse se gestiscono dati non correlati (es. un useState per il nome e uno per il caricamento di una pagina), piuttosto che creare un unico oggetto gigante.

Q: Qual è la differenza tra useState e una variabile globale?

Una variabile globale non provoca il re-render del componente quando cambia. useState è legato al ciclo di vita di React e assicura che l'interfaccia utente sia sempre sincronizzata con i dati.

Prossimi Passi

Ora che hai padroneggiato useState, sei pronto per esplorare concetti più avanzati per rendere le tue app ancora più potenti:

  1. useEffect: Impara a gestire gli "effetti collaterali", come chiamare un'API quando il componente viene caricato.
  2. useContext: Scopri come condividere lo stato tra molti componenti senza dover passare le props manualmente (il cosiddetto "prop drilling").
  3. useReducer: Quando lo stato diventa troppo complesso (molte proprietà correlate), useReducer è l'alternativa professionale a useState.
  4. State Management Esterno: Esplora librerie come Redux Toolkit o Zustand per applicazioni di grandi dimensioni.

La pratica è l'unica via per l'apprendimento. Prova a costruire una semplice "To-Do List" dove puoi aggiungere e rimuovere elementi: userai useState per gestire l'array di task e l'input di testo. Buon coding!