Stato in React: da useState allo stato globale (e quando evitare le librerie)

Lo stato è la causa principale del caos in React. Qui trovi useState, dove far vivere i dati, stato derivato, useEffect, Context, Redux, Zustand e quando una libreria di state management non serve.

Se React a un certo punto ti è sembrato confuso, ingestibile o “magico”, molto probabilmente il problema non era React.

👉 Era lo stato.

La maggior parte dei progetti React non diventa caotica per colpa dei componenti,
ma perché lo stato cresce senza una strategia chiara.

All’inizio tutto funziona.
Poi arrivano:

  • dati che devono essere condivisi
  • componenti che dipendono l’uno dall’altro
  • aggiornamenti che rompono parti lontane

Ed è lì che nasce la sensazione di perdita di controllo.

In questa guida:


Cos’è davvero lo stato (e cosa non è)

Errore comune:

“Lo stato è una variabile che cambia.”

No.

In React, lo stato è:

la fonte di verità che determina cosa viene renderizzato

Questo significa una cosa cruciale:

  • se lo stato è confuso, l’interfaccia sarà confusa
  • se lo stato è sparso, il comportamento sarà imprevedibile

Lo stato non serve a memorizzare valori.
Serve a guidare il rendering.

Questo è coerente con il modello dichiarativo spiegato nella documentazione ufficiale quando si parla di pensare in React: l’interfaccia è sempre una conseguenza dei dati.


useState: la base dello stato in React

Nella guida a React per chi viene da HTML e CSS abbiamo visto le fondamenta: JSX, componenti funzionali e flusso di dati unidirezionale tramite le props. Le props però rappresentano solo dati che arrivano dal genitore. E se un componente avesse bisogno di tracciare modifiche interne, come il conteggio dei “Mi piace” o il valore di un campo di input?

La risposta è lo Stato (State). Lo Stato è il cuore pulsante della dinamicità in React: è un oggetto di memoria interna che, quando viene modificato, costringe React a re-renderizzare il componente e a ricalcolare il Virtual DOM.

Prima dell’introduzione degli Hooks (React 16.8), la gestione dello stato era appannaggio esclusivo dei Componenti basati su Classi. Oggi, l’Hook useState è il metodo standard per dotare i Componenti Funzionali di memoria interna.

Sintassi e flusso di lavoro

useState è una funzione che, quando richiamata, restituisce un array con esattamente due elementi. È prassi comune usare la destrutturazione degli array per accedere a questi due valori:


    import React, { useState } from 'react';

    const [valore, setValore] = useState(valoreIniziale);
    

Cosa significano i due elementi restituiti?

  1. valore: La variabile di stato corrente (il valore che stai leggendo).
  2. setValore: La funzione che devi usare per aggiornare quel valore. Mai modificare valore direttamente!

Il valoreIniziale è l’unico argomento che passi a useState ed è utilizzato solo al primo render del componente.

Esempio pratico: un contatore

Vediamo l’applicazione più semplice, un contatore, che illustra perfettamente come lo Stato attiva il re-render:


    const Contatore = () => {
      // Stato iniziale: 0
      const [conteggio, setConteggio] = useState(0);

      const incrementa = () => {
        // Usa SEMPRE la funzione setter per cambiare lo stato
        setConteggio(conteggio + 1); 
      };

      return (
        <div>
          <p>Il conteggio attuale è: {conteggio}</p>
          <button onClick={incrementa}>Aumenta</button>
        </div>
      );
    };
    

Quando l’utente clicca sul pulsante, viene eseguita incrementa(), che chiama setConteggio(). React riconosce il cambiamento e riesegue l’intera funzione Contatore(), aggiornando il valore visualizzato a schermo.

Gestire gli eventi: il ponte tra utente e stato

Gestire gli eventi (click, submit, change) in React è molto simile a come faresti con l’HTML puro, ma con alcune differenze chiave che mantengono la coerenza con JSX.

Differenze chiave con HTML

  • CamelCase: I nomi degli eventi sono scritti in camelCase (e.g., onClick, non onclick).
  • Passaggio di Funzioni: Non passi una stringa di codice JavaScript come faresti in HTML (e.g., onclick="doSomething()"). Invece, passi una funzione JavaScript all’attributo, racchiusa tra graffe (e.g., onClick={doSomething}).
  • Oggetto Evento Sintetico: React passa un SyntheticEvent che standardizza il comportamento degli eventi su tutti i browser.

Esempio: Se devi passare parametri all’handler (alla funzione che gestisce l’evento), devi avvolgere la chiamata in una funzione anonima (una arrow function):


    // Se la funzione ha bisogno di argomenti, usa una funzione anonima
    <button onClick={() => handleAction('delete')}>Elimina</button>
    

Errori comuni con useState

Quando si inizia con lo Stato, gli sviluppatori junior commettono spesso questi errori, che portano a bug insidiosi:

  • Mai modificare lo Stato direttamente: Scrivere conteggio = 5; non attiverà il re-render. Devi sempre usare la funzione setter (setConteggio(5)).
  • Aggiornamento Asincrono: Le chiamate a setState o setValore sono asincrone. Se il nuovo stato dipende dal valore precedente (come nel nostro contatore), dovresti passare una funzione di callback al setter per garantire che si basi sullo stato più aggiornato.

    // Modalità Sicura per gli aggiornamenti basati sullo stato precedente
    const incrementaSicuro = () => {
      setConteggio(prevConteggio => prevConteggio + 1); 
    };
    

Utilizzando la forma setValore(funzione), si riceve in input lo stato precedente (prevConteggio), eliminando il rischio di race condition (condizione di gara) dovute all’asincronia.

📌 Risorsa Aggiuntiva: Salva e riapri questa guida ogni volta che devi implementare un contatore, un toggle o un campo form in React. Esercitati a usare sempre la forma funzionale del setter (setValore(prev => ...)) per gli aggiornamenti più sicuri.


Il vero problema: troppo stato (e nel posto sbagliato)

La causa numero uno del caos non è “mancanza di Redux” o “hook sbagliati”.

È questo:

mettere stato ovunque, senza chiedersi perché

Segnali che stai andando nella direzione sbagliata:

  • lo stesso dato esiste in più componenti
  • passi props in profondità “solo per arrivare sotto”
  • aggiorni uno stato e qualcosa di distante si rompe

Quando succede, non serve un nuovo tool.
Serve ridisegnare la responsabilità dello stato.


Regola fondamentale: lo stato vive dove serve

Principio chiave:

lo stato deve vivere nel componente più alto che ne ha realmente bisogno

Non più in alto.
Non più in basso.

Questo principio è spiegato chiaramente anche nella guida MDN su React, quando si introduce la gestione dello stato e il passaggio dei dati.

Ignorare questa regola porta a:

  • prop drilling
  • duplicazione
  • bug difficili da tracciare

Stato locale: il tuo miglior alleato (se non lo tradisci)

Lo stato locale (useState) non è una soluzione di serie B.
È lo strumento principale.

Usalo per:

  • input
  • toggle
  • stati temporanei
  • UI state

Se inizi a spostare stato verso l’alto “per sicurezza”, stai creando complessità preventiva.

La semplicità viene prima della riusabilità.


Quando lo stato deve salire (e quando no)

Una buona domanda da farti è questa:

“Chi ha davvero bisogno di sapere questa informazione?”

Se la risposta è:

  • “solo questo componente” → resta
  • “più parti della stessa vista” → sale
  • “parti lontane dell’app” → centralizza

Salire senza motivo crea componenti “contenitori” vuoti.
Scendere senza criterio crea dipendenze nascoste.

Entrambi portano caos.


Stato derivato: uno degli errori più costosi

Errore tipico:

“Salvo nello stato qualcosa che posso calcolare.”

Questo è uno dei bug architetturali più diffusi.

Se un valore:

  • può essere calcolato da props o stato
  • non serve essere persistito

👉 non è stato

Salvare stato derivato significa:

  • duplicare fonti di verità
  • introdurre sincronizzazioni inutili
  • creare bug intermittenti

React funziona meglio quando lo stato è minimo e necessario.


useEffect non serve a “far funzionare le cose”

A volte, dopo che il componente è stato renderizzato (o ri-renderizzato a causa di un cambio di stato), è necessario eseguire un’azione secondaria. Queste sono chiamate side effect (effetti collaterali): recuperare dati da API, aggiornare il titolo della pagina, impostare timer, ecc.

In React, gli effetti collaterali si gestiscono con l’hook useEffect.

Se useState gestisce “la memoria” del componente, useEffect gestisce “la vita” del componente (il montaggio, l’aggiornamento e lo smontaggio).

Per ora, ricorda che ogni volta che devi “parlare” con il mondo esterno (API, browser DOM), devi usare useEffect. Per l’uso pratico di useEffect e del suo array di dipendenze trovi una guida dedicata: Hook useState e useEffect spiegati semplice.

Altro errore grave:

“Uso useEffect per sistemare lo stato.”

useEffect non è un collante.
È uno strumento per gestire effetti collaterali.

Serve per:

  • side effects
  • fetch di dati
  • integrazioni esterne

Non serve per:

  • sincronizzare stato
  • “aggiustare” il rendering
  • replicare lifecycle complessi

Se useEffect diventa la colla che tiene tutto insieme,
stai coprendo un problema architetturale.

Questo è uno dei motivi per cui molti lo trovano “confuso”: lo incontrano quando il design è già fragile.


Quali hook servono davvero (e quali puoi ignorare)

Quando inizi a usare React, gli hook sembrano ovunque.
Liste infinite, tutorial che ne usano dieci insieme, esempi che ti fanno pensare:

“Se non li conosco tutti, non sto davvero usando React.”

È falso.
Ed è uno dei motivi principali per cui molti sviluppatori si perdono inutilmente.

La verità è semplice:
👉 React richiede pochissimi hook per essere usato bene.
Il resto è contesto, maturità del progetto o overengineering.

Il problema non sono gli hook, è l’ansia da completezza

Chi arriva da HTML, CSS o JavaScript “classico” tende a fare questo errore:

  • vuole capire tutto subito
  • studia API prima di avere problemi reali
  • usa hook perché “si fa così”

Ma React non premia chi conosce più hook.
Premia chi prende decisioni semplici sullo stato e sul flusso dei dati.

Questo è coerente con l’approccio spiegato nella documentazione ufficiale quando si introduce il pensare in React: prima capire il modello, poi gli strumenti.

Gli unici hook davvero fondamentali all’inizio

Se stai costruendo interfacce standard, questi hook ti bastano per molto tempo.

  • useState: lo stato locale, visto sopra
  • useEffect, ma con cautela: solo per gli effetti collaterali
  • useContext: solo quando smetti di passare props a cascata (lo vediamo nella sezione sullo stato globale)

Se non hai chiaro quando usare useState, il problema non è il framework, è il modello mentale.

👉 La maggior parte dei problemi di React nasce da troppo stato, non da poco.

Hook che puoi tranquillamente ignorare all’inizio

Qui è dove molti perdono tempo.

useReducer

Potente, sì.
Necessario subito? No.

Ha senso quando:

  • la logica di stato è complessa
  • le transizioni sono molte
  • vuoi esplicitare gli eventi

Se lo usi per “ordine mentale”, stai anticipando complessità che forse non arriverà.


useMemo e useCallback

Questi hook non servono per scrivere React migliore.
Servono per ottimizzare React che già funziona.

Usarli troppo presto:

  • rende il codice più verboso
  • nasconde problemi reali
  • complica la lettura

Se il tuo primo pensiero è “devo memoizzare”, probabilmente stai ottimizzando qualcosa che non è un problema.


Hook personalizzati

Gli hook custom non sono un obiettivo.
Sono una conseguenza.

Hanno senso quando:

  • stai duplicando logica
  • il comportamento è chiaro
  • il flusso è stabile

Se li crei “per pulizia”, rischi solo di astrarre troppo presto.


Prima di aggiungere un hook: una domanda

Prima di introdurre un nuovo hook, chiediti:

“Questo risolve un problema reale che ho adesso?”

Se la risposta è:

  • “no, ma potrebbe servire” → aspetta
  • “lo fanno tutti” → ignora
  • “ora il codice è più chiaro” → forse sì

React diventa caotico quando:

  • accumuli strumenti
  • anticipi problemi
  • moltiplichi concetti

Diventa semplice quando:

  • lo stato è chiaro
  • il flusso è leggibile
  • gli hook sono pochi

Perché meno hook = più controllo

Ogni hook che aggiungi:

  • introduce un livello mentale
  • richiede disciplina
  • aumenta il costo di manutenzione

Se non ne senti il bisogno, non aggiungerlo.

Questo approccio è coerente anche con le linee guida ufficiali di React sulla gestione degli effetti e dello stato, presenti nella documentazione MDN e su react.dev, dove l’enfasi è sempre sulla chiarezza prima dell’ottimizzazione.


Quando ha senso centralizzare lo stato

Centralizzare non è sbagliato.
È sbagliato farlo troppo presto.

Ha senso quando:

  • più parti lontane condividono dati
  • il flusso è bidirezionale
  • il dato è davvero globale
  • il dominio è stabile
  • il team conosce bene lo strumento

Non perché:

  • “così è più ordinato”
  • “forse servirà in futuro”
  • “lo fanno tutti”

Redux, Context, store esterni non risolvono il caos.
Lo amplificano se lo porti dentro senza un modello mentale chiaro.


Gestire lo stato globale React è una delle sfide più comuni nello sviluppo di applicazioni complesse. Quando i componenti devono condividere dati, passare props attraverso molteplici livelli (noto come “prop drilling”) diventa inefficiente e difficile da mantenere. Questo articolo esplora le principali soluzioni per creare uno stato centralizzato, confrontando l’approccio nativo con le librerie più popolari.

Il problema del prop drilling

Immagina un’applicazione React con una struttura di componenti annidati. Se un componente genitore al livello più alto possiede un dato (es. l’utente autenticato) che serve a un componente figlio molto in profondità, quel dato deve essere passato come prop attraverso tutti i componenti intermedi. Questo processo, chiamato prop drilling, rende il codice verboso e accoppia strettamente componenti che non utilizzano direttamente quei dati.

La soluzione è creare uno “store” globale, un contenitore di stato accessibile da qualsiasi componente dell’applicazione senza dover passare props manualmente. Vediamo le opzioni principali.

Context API: la soluzione nativa di React

React offre una soluzione integrata per la gestione dello stato globale: la Context API. Introdotta per evitare il prop drilling, permette di creare un “contesto” che espone dati a tutti i componenti discendenti che ne fanno richiesta, utilizzando gli hook createContext e useContext.

Pro e contro di Context API

  • Pro: Soluzione nativa, non richiede dipendenze esterne.
  • Pro: Semplice da implementare per casi d’uso non troppo complessi.
  • Contro: Può causare ri-render non necessari nei componenti che consumano il contesto, anche se il dato specifico che usano non è cambiato.
  • Contro: Meno ottimizzato per aggiornamenti di stato ad alta frequenza rispetto a librerie dedicate.

Esempio pratico con useContext

Prima creiamo un contesto per il tema dell’applicazione.


// ThemeContext.js
import { createContext, useState } from 'react';

export const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};
        

Poi, avvolgiamo la nostra App nel Provider.


// App.js
import { ThemeProvider } from './ThemeContext';
import Toolbar from './Toolbar';

function App() {
  return (
    <ThemeProvider>
      <Toolbar />
    </ThemeProvider>
  );
}
        

Infine, un componente può consumare il contesto con l’hook useContext.


// Toolbar.js
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

function Toolbar() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
      <p>Il tema attuale è: {theme}</p>
      <button onClick={toggleTheme}>Cambia Tema</button>
    </div>
  );
}
        

useContext (solo quando smetti di passare props a cascata)

useContext ha senso quando:

  • il prop drilling diventa rumoroso
  • più componenti lontani leggono gli stessi dati
  • il contesto è davvero condiviso

Non è un sostituto di useState.
È solo un modo per distribuire lo stato, non per crearne di nuovo.

Redux: lo standard per applicazioni complesse

Redux è una libreria esterna che implementa un pattern prevedibile per la gestione dello stato. Si basa su tre principi fondamentali: una singola fonte di verità (lo store), stato di sola lettura (modificabile solo tramite “azioni”) e modifiche effettuate con funzioni pure (i “reducers”).

Oggi, l’approccio consigliato è usare Redux Toolkit, che semplifica notevolmente la configurazione e riduce il codice boilerplate.

Pro e contro di Redux

  • Pro: Estremamente prevedibile e facile da debuggare grazie ai Redux DevTools.
  • Pro: Ottimizzato per performance elevate e aggiornamenti frequenti.
  • Pro: Vasto ecosistema di middleware (es. Redux Thunk, Redux Saga).
  • Contro: Curva di apprendimento più ripida rispetto a Context API.
  • Contro: Può risultare eccessivo per progetti di piccole dimensioni.

Esempio pratico con Redux Toolkit

Definiamo una “slice” dello stato che contiene il reducer e le azioni relative a un contatore.


// features/counter/counterSlice.js
import { createSlice } from '@reduxjs/toolkit';

export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0,
  },
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
  },
});

export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
        

Un componente può interagire con lo store usando gli hook useSelector e useDispatch.


// Counter.js
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './features/counter/counterSlice';

export function Counter() {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <button onClick={() => dispatch(increment())}>Incrementa</button>
      <span>{count}</span>
      <button onClick={() => dispatch(decrement())}>Decrementa</button>
    </div>
  );
}
        

Alternative moderne: Zustand e Jotai

Negli ultimi anni sono emerse librerie più leggere e minimaliste come Zustand e Jotai (Recoil, citato spesso nei tutorial, è stato archiviato da Meta all’inizio del 2025 e non è più mantenuto). Zustand, in particolare, ha guadagnato popolarità per la sua semplicità e per un’API basata sugli React Hooks che riduce drasticamente il boilerplate.

Zustand unisce la semplicità di Context con le performance ottimizzate di Redux, evitando il problema dei ri-render non necessari.

Esempio pratico con Zustand

Creare uno store in Zustand richiede poche righe di codice.


// store.js
import { create } from 'zustand';

const useStore = create((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
}));

export default useStore;
        

Usarlo in un componente è ancora più semplice.


// BearCounter.js
import useStore from './store';

function BearCounter() {
  const bears = useStore((state) => state.bears);
  return <h1>{bears} orsi intorno.</h1>;
}

function Controls() {
  const increasePopulation = useStore((state) => state.increasePopulation);
  return <button onClick={increasePopulation}>Aggiungi un orso</button>;
}
        

Quale soluzione scegliere per lo stato globale in React?

La scelta della giusta soluzione per la gestione dello stato globale React dipende dalle esigenze specifiche del progetto:

  • Context API: Ideale per applicazioni piccole o per gestire stati semplici e con pochi aggiornamenti (es. tema, lingua, utente autenticato).
  • Redux: La scelta più solida per applicazioni di grandi dimensioni, con logiche di stato complesse, e dove la prevedibilità e il debugging sono critici.
  • Zustand: Un’ottima via di mezzo. Offre un’esperienza di sviluppo rapida e performance elevate, perfetta per progetti di medie dimensioni o per chi cerca un’alternativa meno verbosa a Redux.

Analizzare la complessità e la scala futura della tua applicazione è il primo passo per implementare una gestione dello stato robusta, manutenibile ed efficiente.


Quando non usare una libreria di state management

In React, una delle decisioni più frequenti — e più sbagliate — è introdurre una libreria di state management troppo presto.

Redux, Zustand, Jotai e simili non sono il problema.
Il problema è quando e perché vengono usate.

In questa sezione vediamo quando non usare una libreria di state management in React, quali segnali indicano che stai anticipando una complessità inutile e come evitare di rallentare il progetto invece di semplificarlo.

React non ha un “buco” da riempire

React nasce con:

  • stato locale (useState)
  • derivazione dello stato
  • composizione dei componenti
  • flusso di dati unidirezionale

Questo significa una cosa molto semplice:

Se non senti chiaramente il limite, probabilmente non esiste.

Molti team introducono una libreria globale per abitudine, non per necessità.

1️⃣ Quando lo stato è locale o chiaramente confinato

Se lo stato:

  • riguarda un singolo componente
  • o una piccola porzione dell’albero
  • o può essere passato come props senza frizioni

👉 NON è stato globale.

Introdurre una libreria in questo caso:

  • aumenta il numero di concetti
  • nasconde la responsabilità reale
  • rende il flusso meno leggibile

Stato locale ben gestito batte stato globale prematuro.


2️⃣ Quando stai usando la libreria come “colla”

Se la libreria serve a:

  • evitare di pensare alla struttura
  • collegare componenti senza una gerarchia chiara
  • far “parlare” parti che non dovrebbero conoscersi

allora stai usando lo stato globale come scorciatoia architetturale.

👉 Questo è un anti-pattern, non una soluzione.


3️⃣ Quando il dominio applicativo è ancora instabile

Se:

  • i requisiti cambiano spesso
  • il dominio non è chiaro
  • le responsabilità non sono ancora definite

aggiungere una libreria globale congela decisioni sbagliate.

In questa fase serve:

  • flessibilità
  • leggibilità
  • possibilità di cambiare direzione

Non una struttura rigida difficile da smontare.


4️⃣ Quando stai ottimizzando prima di avere problemi

Molti pensano:

“La useremo quando il progetto crescerà”

Ma la realtà è questa:

  • la libreria viene introdotta
  • la complessità cresce subito
  • il beneficio arriva (forse) molto dopo

👉 State management globale senza un problema reale = debito tecnico anticipato.


5️⃣ Quando React già risolve il problema

Spesso il bisogno percepito è:

  • condividere dati
  • sincronizzare UI
  • reagire a eventi

Ma React offre già:

  • lifting dello stato
  • context API (con criterio)
  • composizione

Se puoi risolvere il problema con gli strumenti nativi, far entrare una libreria esterna è solo rumore.

Segnali che stai introducendo una libreria troppo presto

Se ti riconosci in uno di questi punti, fermati:

  • “Così siamo più scalabili” (ma non sai in cosa)
  • “Lo usiamo in tutti i progetti”
  • “Lo vedo in molti tutorial”
  • “Così è più professionale”

👉 Nessuno di questi è un requisito tecnico.


Un modello semplice che funziona (quasi sempre)

Prima di aggiungere strumenti, assicurati che:

  1. lo stato è minimo
  2. ogni stato ha un solo proprietario
  3. i dati fluiscono dall’alto verso il basso
  4. le azioni risalgono in modo esplicito

Questo modello è sufficiente per la maggior parte delle applicazioni React.

Se non funziona, il problema non è “React troppo semplice”.
È che l’app è davvero complessa — e allora servono strumenti, non prima.


Conclusione: lo stato non è il problema, è lo specchio

Lo stato in React non crea caos.
Rivela il caos.

Se:

  • lo stato è chiaro
  • le responsabilità sono definite
  • il flusso è leggibile

React diventa prevedibile.

Se invece lo stato:

  • è duplicato
  • è sparso
  • viene “aggiustato” con effetti

allora nessun framework ti salverà.

Gestire bene lo stato non significa conoscere più hook.
Significa prendere decisioni chiare su cosa conta davvero.

Le librerie di state management in React non sono un upgrade automatico.

Sono uno strumento potente che:

  • risolve problemi reali
  • ma amplifica quelli architetturali

👉 Se il tuo progetto è semplice, lascialo semplice.
👉 Se non riesci a spiegare perché ti serve una libreria, non ti serve.

👉 Non serve conoscere tutti gli hook.
Serve sapere quando NON usarli.