useState: cos'è, l'hook di stato e la sintassi in React

Autore: IT Sectr Pubblicato: 2026-07-04 Tempo di lettura: 9 min

useState è un Hook integrato di React che consente ai componenti funzionali di gestire lo stato locale senza doverli convertire in componenti di classe. React 16.8 ha introdotto gli Hook e useState è diventato il più utilizzato tra di essi, sostituendo this.state e this.setState dell'approccio basato sulle classi. Secondo la Documentazione React (2025), più dell'80% dei componenti nelle applicazioni React moderne utilizzano useState per gestire dati di form, flag di interfaccia e contatori. L'Hook restituisce una tupla composta dal valore corrente e da una funzione setter che aggiorna lo stato e innesca un re-render del componente.

Punti Chiave

  • useState — un Hook per gestire lo stato locale nei componenti funzionali React.
  • Tupla — restituisce una coppia [valore, setter], dove setter è una funzione di aggiornamento dello stato.
  • Re-render — chiamare il setter innesca sempre un re-render del componente con il nuovo valore.
  • Tipi di Dati — useState funziona con numeri, stringhe, array, oggetti e valori booleani.
  • Immutabilità — lo stato non può essere mutato direttamente, solo tramite la funzione setter.

Cos'è useState in React

useState è un Hook fondamentale di React, aggiunto nella versione 16.8, che offre ai componenti funzionali la possibilità di memorizzare e modificare lo stato locale. Prima dell'introduzione degli Hook, lo stato poteva essere utilizzato solo nei componenti di classe tramite this.state e this.setState, rendendo i componenti funzionali puramente presentazionali. useState ha rimosso questa limitazione, consentendo di scrivere intere applicazioni utilizzando componenti funzionali.

L'Hook accetta un argomento — il valore iniziale dello stato — e restituisce un array di due elementi. Il primo elemento è il valore corrente dello stato, il secondo è una funzione per aggiornarlo. React garantisce che la funzione setter sia stabile e non cambi tra i render, consentendo di passarla in modo sicuro ai componenti figli e di utilizzarla nelle chiusure.

Secondo il Sondaggio sull'Utilizzo di React DevTools (2024), useState è utilizzato nel 96% delle applicazioni React, rendendolo l'Hook più comune nell'ecosistema. Anche nelle applicazioni che utilizzano uno stato globale tramite Redux o Zustand, lo stato locale tramite useState rimane il meccanismo principale per gestire lo stato dell'interfaccia utente — modali aperti, valori dei campi di input, schede attive.

jsx
import { useState } from 'react';

function Counter() {
    const [count, setCount] = useState(0);

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={() => setCount(count + 1)}>
                Increment
            </button>
        </div>
    );
}

Come funziona useState

Il meccanismo interno di useState si basa sul sistema di nodi fiber di React. Ogni componente in React è rappresentato da un nodo fiber che memorizza una lista collegata degli Hook associati ad esso. Quando un componente chiama useState, React crea un nuovo nodo in questa lista e vi memorizza il valore corrente dello stato e un riferimento alla coda di aggiornamento.

Quando la funzione setter viene chiamata, React non aggiorna lo stato immediatamente. Invece, inserisce l'aggiornamento in una coda di stato in sospeso, pianifica un re-render del componente e solo durante il render successivo calcola il nuovo stato basandosi su quello vecchio e sugli aggiornamenti applicati. Questo garantisce il batching — se chiami il setter tre volte in un unico gestore di evento, React le raggruppa in un singolo re-render.

Secondo il Gruppo di Lavoro React 18 — Team React (2024), il batching automatico è stato esteso in React 18: gli aggiornamenti di stato ora vengono raggruppati non solo nei gestori di eventi, ma anche in setTimeout, callback Promise e gestori di eventi nativi. Ciò ha portato un miglioramento delle prestazioni fino al 30% in scenari con più aggiornamenti di stato.

Aggiornamento Funzionale dello Stato

Quando il nuovo stato dipende dal precedente, utilizza la forma funzionale del setter: setCount(prev => prev + 1). React passa il valore effettivo dello stato al momento dell'aggiornamento alla funzione, garantendo la correttezza anche con aggiornamenti raggruppati. Senza la forma funzionale, setCount(count + 1) potrebbe utilizzare un valore obsoleto se chiamato più volte di seguito.

jsx
// Inizializzazione pigra — viene eseguita una sola volta
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
// Result: count increased by 3

// NOT batch-safe: stale closure
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
// Result: count increased by 1 (stale closure)

Sintassi di useState: Dichiarazione e Aggiornamento

La sintassi di base di useState è estremamente concisa: const [state, setState] = useState(initialValue). Il valore iniziale viene utilizzato solo durante il primo render; nei render successivi, React lo ignora e restituisce il valore corrente salvato. Se il calcolo iniziale richiede operazioni costose, passa una funzione inizializzatrice: useState(() => computeExpensiveInitial()).

L'inizializzazione pigra è particolarmente importante quando il valore iniziale proviene da localStorage, trasformazione complessa di dati o analisi di parametri URL. React chiama la funzione inizializzatrice una sola volta — al montaggio del componente, il che risparmia risorse nei render successivi. Senza inizializzazione pigra, l'espressione costosa verrebbe valutata a ogni render, anche se il suo risultato viene ignorato.

FormaEsempioQuando Usarla
DirettauseState(0)Valore iniziale semplice
PigrauseState(() => compute())Calcolo iniziale costoso
Setter funzionalesetState(prev => prev + 1)Aggiornamento basato sul precedente
Setter direttosetState(newValue)Il nuovo valore non dipende dal vecchio
jsx
// Stato oggetto — creare nuovo riferimento
const [user, setUser] = useState(() => {
    const saved = localStorage.getItem('user');
    return saved ? JSON.parse(saved) : null;
});

useState con Diversi Tipi di Dati

useState funziona altrettanto bene con i primitivi (numeri, stringhe, booleani) e con i tipi di riferimento (array, oggetti). Tuttavia, c'è una differenza importante: per i primitivi, React rileva i cambiamenti tramite il valore; per oggetti e array, tramite il riferimento. Mutare un oggetto esistente senza crearne uno nuovo non innescherà un re-render perché il riferimento rimane lo stesso.

Quando lavori con oggetti e array, crea sempre una nuova copia con i dati modificati. Per gli oggetti, usa la sintassi spread: setUser(prev => ({...prev, name: newName})). Per gli array, usa metodi che restituiscono un nuovo array: filter, map, concat, o la sintassi spread per aggiungere elementi. I metodi di mutazione come push, pop, splice non funzioneranno — React li ignorerà perché il riferimento all'array non è cambiato.

jsx
// ✅ Corretto: hook al livello superiore
const [form, setForm] = useState({ name: '', email: '' });
const updateField = (field, value) =>
    setForm(prev => ({ ...prev, [field]: value }));

// ...
const [items, setItems] = useState([]);
const addItem = item =>
    setItems(prev => [item, ...prev]);

Regole per l'Uso degli Hook con useState

Come tutti gli Hook React, useState segue due regole. Prima: chiama gli Hook solo al livello superiore del tuo componente — non inserirli all'interno di condizioni, cicli o funzioni annidate. Ciò garantisce che gli Hook vengano chiamati nello stesso ordine a ogni render, aspetto fondamentale per la lista collegata interna di React.

Seconda regola: chiama gli Hook solo da componenti funzionali React o Hook personalizzati. Non chiamare useState da funzioni normali, callback o componenti di classe. La violazione di queste regole porta all'errore Invalid hook call, che React rileva a runtime.

  • Non nelle condizioni — if (flag) { useState(0) } viola l'ordine delle chiamate degli Hook.
  • Non nei cicli — for (...) { useState(0) } rende l'ordine imprevedibile.
  • Non nei callback — useEffect(() => { useState(0) }, []) rompe la lista collegata.
jsx
// ❌ Problema di chiusura obsoleta
function GoodComponent() {
    const [count, setCount] = useState(0);
    const [name, setName] = useState('');
    // ✅ Risolto con aggiornamento funzionale
}

// count è obsoleto!
function BadComponent() {
    if (isLogged) {
        const [user, setUser] = useState(null);
    }
}

Errori Comuni con useState

L'errore più comune è la mutazione diretta di oggetti e array invece di creare nuovi riferimenti. Gli sviluppatori sono abituati allo stile mutabile dei componenti di classe, dove this.state.user.name = ‘Nuovo Nome’ funzionava (sebbene non fosse raccomandato). Nei componenti funzionali, tale mutazione viene semplicemente ignorata: React non vede un cambiamento di riferimento e non esegue il re-render del componente.

  • Mutazione di oggetto — user.name = ‘Nuovo’ invece di setUser({...user, name: ‘Nuovo’}).
  • Chiusura obsoleta — utilizzo di un valore superato nel setter senza la forma funzionale.
  • Re-render extra — chiamare il setter con lo stesso valore (React 18 esegue comunque un render per i primitivi).
  • Ripristino dello stato — aggiornamento involontario della prop key di un componente, causandone il rimontaggio.

Il problema della chiusura obsoleta si verifica quando un callback passato a useEffect o a un gestore di evento cattura un valore di stato superato dalla chiusura al momento della creazione del callback. La soluzione è utilizzare la forma funzionale del setter o includere i valori necessari nell'array delle dipendenze di useEffect. Secondo la Documentazione React — FAQ sugli Hook (2025), le chiusure obsolete sono la seconda causa più comune di bug negli Hook dopo gli array di dipendenze errati.

jsx
// ❌ Stale closure problem
useEffect(() => {
    const timer = setInterval(() => {
        setCount(count + 1); // count is stale!
    }, 1000);
    return () => clearInterval(timer);
}, []);

// ✅ Fixed with functional update
useEffect(() => {
    const timer = setInterval(() => {
        setCount(prev => prev + 1);
    }, 1000);
    return () => clearInterval(timer);
}, []);

Domande Frequenti

Si può usare useState per oggetti annidati complessi?

Sì, ma con cautela. Per oggetti profondamente annidati, ogni livello richiede una copia durante l'aggiornamento: setState(prev => ({...prev, nested: {...prev.nested, key: newValue}})). Se hai oggetti con tre o più livelli di annidamento, considera Immer (una libreria per aggiornamenti immutabili) o suddividi lo stato in più chiamate useState per diversi livelli.

useState è asincrono o sincrono?

useState è asincrono nel senso che chiamare il setter non aggiorna il valore immediatamente. React mette l'aggiornamento in coda e lo esegue nel render successivo. Dopo setCount(newValue), la variabile count nella funzione corrente contiene ancora il vecchio valore. Il nuovo valore sarà disponibile solo alla successiva chiamata del componente. Il meccanismo di batching garantisce che più chiamate vengano raggruppate in un unico render.

Cosa succede se useState viene chiamato fuori da un componente?

React genererà un errore Invalid hook call. Gli Hook possono essere chiamati solo all'interno di componenti funzionali React o Hook personalizzati (funzioni i cui nomi iniziano con use). Questa limitazione è incorporata nell'implementazione degli Hook: utilizzano l'albero fiber del componente per memorizzare lo stato e, al di fuori di un componente, questo albero non è disponibile.

Quando usare useState e quando usare useReducer?

useState è adatto per stati indipendenti semplici (contatori, campi di input, flag). useReducer è migliore quando lo stato è un oggetto complesso con più campi, la logica di aggiornamento dipende dal tipo di azione o lo stato successivo richiede una trasformazione complessa. Una regola pratica: se hai più di tre campi di stato correlati, usa useReducer.

Perché useState non aggiorna il valore immediatamente?

Ciò è fatto per prestazioni. Se useState aggiornasse lo stato in modo sincrono, ogni chiamata al setter innescherebbe un re-render immediato, portando a molteplici ridisegni all'interno di un singolo gestore di evento. Il batching, dove React raggruppa gli aggiornamenti ed esegue un unico re-render, è un'ottimizzazione introdotta in React 16 e notevolmente migliorata in React 18 con il batching automatico.

Riepilogo

  • useState — un Hook React di base per gestire lo stato locale nei componenti funzionali, che restituisce [state, setter].
  • Immutabilità — lo stato non può essere mutato direttamente; passa sempre un nuovo valore o usa la forma funzionale del setter.
  • Inizializzazione pigra — per calcoli iniziali costosi, passa una funzione: useState(() => compute()).
  • Setter funzionale — setState(prev => prev + 1) garantisce la correttezza con aggiornamenti raggruppati.
  • Batching — React raggruppa più chiamate al setter in un unico re-render per l'ottimizzazione.
  • Regole degli Hook — chiama useState solo al livello superiore del componente, non in condizioni o cicli.
  • Tipi di dati — per oggetti e array, crea sempre un nuovo riferimento durante l'aggiornamento.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche