React
Stato derivato: smettere di copiare le props
Lo stato derivato è un valore che si calcola da props o da altro stato: copiarlo in useState crea una seconda fonte di verità che prima o poi diverge.
In sintesi
- Un valore calcolabile durante il render non ha bisogno di useState né di un effect di sincronizzazione.
- Copiare le props in stato produce un render in più e un frame con il valore vecchio.
- Per ripartire da zero quando cambia l’entità basta cambiare la key del componente, senza effect.
Lo stato derivato è un valore che si calcola dalle props o da altro stato. Se lo copi in useState crei una seconda fonte di verità, che resta indietro appena le props cambiano.
Il sintomo
Il codice tipico è un useEffect che copia una prop nello stato ogni volta che la prop cambia. Funziona finché la prop non cambia due volte nello stesso commit, e costa un render extra: il primo mostra il valore vecchio, il secondo quello nuovo.
La correzione
Se il valore si può calcolare durante il render, calcolalo: nessun effect, nessun render extra, nessuno stato da sincronizzare.
function SearchList({ items, query }) {
const visible = items.filter((item) =>
item.name.toLowerCase().includes(query.toLowerCase()),
);
return (
{visible.map((item) => (
{item.name}
))}
);
}
Quando lo stato serve davvero
- Input dell’utente: un campo scrive in stato, non deriva da una prop.
- Reset per entità: quando la prop identifica un’altra entità, cambia la
keydel componente e lo stato riparte da zero, senza effect. - Valore iniziale: se serve solo il primo valore della prop, passalo a
useStatee non riscriverlo dopo.
Il costo del calcolo
Filtrare mille elementi a ogni tasto è lavoro inutile solo se il render è frequente: prima misura, poi valuta useMemo. La memoizzazione aggiunge una lista di dipendenze da mantenere, e sbagliarla produce lo stesso difetto dello stato copiato, con meno indizi.
Provarlo con i tipi
npm create vite@latest derived-demo -- --template react-ts
cd derived-demo
npm install
npx tsc --noEmit
Il template react-ts di Vite, con licenza MIT, include React 19 e TypeScript; npx tsc --noEmit controlla i tipi senza produrre file e segnala una prop che manca nella derivazione.
Record vivi nel catalogo
Due record dove la derivazione è la norma: filters, che filtra una lista da valori di form, e table, che ordina le righe in base allo stato corrente. Il resto sta tra i 111.985 componenti e i 1.556 blocchi.
La limitazione
Calcolare durante il render funziona solo se il calcolo è puro e non tocca il DOM: una derivazione che legge misure o dati esterni va in un effect. E un calcolo pesante ripetuto a ogni render è un problema di prestazioni, non di architettura.
Dove continuare
L’ordine delle chiamate è in regole degli hook; la funzione stabile da passare ai figli è in useCallback; il confronto con un framework a stato reattivo è in Svelte vs React.
Domande frequenti
Stato derivato e valore calcolato sono la stessa cosa?
Sì. Lo stato derivato è un valore che dipende da altre fonti e che quindi non ha bisogno di essere salvato: si ricalcola durante il render, ogni volta che le fonti cambiano.
Posso sincronizzare una prop con useState in un effect?
Tecnicamente sì, ma è il pattern che il team di React sconsiglia: produce un render in più e può mostrare un valore vecchio. Se il valore è calcolabile, calcolalo; se serve un reset, cambia la key.
Come resetto lo stato quando cambia una prop?
Con la key. Metti key={entity.id} sul componente che contiene lo stato: React lo smonta e lo rimonta quando l’id cambia, e lo stato riparte dai valori iniziali senza un effect.
useMemo sostituisce lo stato derivato?
No, lo ottimizza. Lo stato derivato è la scelta di non salvare il valore; useMemo è una cache opzionale per non ricalcolarlo a ogni render. Prima si toglie lo stato, poi si aggiunge la cache.
Derivare a ogni render è lento?
Dipende dal calcolo e dalla frequenza. Un filtro su cento elementi è irrilevante; un ordinamento su diecimila righe a ogni tasto si misura nel Profiler prima di aggiungere una cache.