I modelli mentali di React possono rendere React più semplice: apprendi le idee chiave su componenti, rendering, stato ed effetti, poi applicale per costruire interfacce velocemente tramite chat.

React può sembrare frustrante all'inizio perché vedi l'interfaccia cambiare, ma non sempre riesci a spiegare perché è cambiata. Clicchi un pulsante, qualcosa si aggiorna e poi una parte diversa della pagina ti sorprende. Di solito non è “React è strano”. È “la mia idea di cosa fa React è sfocata”.
Un modello mentale è la storia semplice che ti racconti su come funziona qualcosa. Se la storia è sbagliata, prenderai decisioni sicure che portano a risultati confusi. Pensa a un termostato: un modello povero è “imposto 22°C e la stanza diventa subito 22°C”. Un modello migliore è “imposto un obiettivo, e il riscaldamento si accende e spegne nel tempo per raggiungerlo”. Con la storia migliore, il comportamento smette di sembrare casuale.
React funziona allo stesso modo. Una volta che adotti poche idee chiare, React diventa prevedibile: puoi guardare i dati correnti e indovinare affidabilmente cosa sarà sullo schermo.
Dan Abramov ha aiutato a diffondere questa mentalità del “rendilo prevedibile”. L'obiettivo non è memorizzare regole. È tenere poche verità in testa così puoi fare debug ragionando, non con tentativi ed errori.
Tieni a mente queste idee:
Conservale e React smetterà di sembrare magia. Comincerà a sembrare un sistema di cui ti puoi fidare.
React diventa più semplice quando smetti di pensare in “schermate” e inizi a pensare in piccoli pezzi. Un componente è un'unità riutilizzabile di UI. Prende input e restituisce una descrizione di come dovrebbe apparire l'interfaccia per quegli input.
Aiuta trattare un componente come una descrizione pura: “dato questo dato, mostra questo”. Quella descrizione può essere usata in molti posti perché non dipende da dove vive.
Le props sono gli input. Vengono da un componente genitore. Le props non sono “possesso” del componente e non sono qualcosa che il componente dovrebbe cambiare di nascosto. Se un pulsante riceve label="Save", il compito del pulsante è renderizzare quell'etichetta, non decidere che dovrebbe essere diversa.
Lo state è dato posseduto. È ciò che il componente ricorda nel tempo. Lo state cambia quando un utente interagisce, quando una richiesta finisce, o quando decidi che qualcosa deve essere diverso. A differenza delle props, lo state appartiene a quel componente (o a qualunque componente tu scelga come proprietario).
Versione semplice dell'idea principale: l'interfaccia è una funzione dello stato. Se lo stato dice “loading”, mostra uno spinner. Se lo stato dice “error”, mostra un messaggio. Se lo stato dice “items = 3”, rendi tre righe. Il tuo compito è fare in modo che l'interfaccia legga dallo stato, non che derivi in variabili nascoste.
Un modo veloce per separare i concetti è:
SearchBox, ProfileCard, CheckoutForm)name, price, disabled)isOpen, query, selectedId)Esempio: una modal. Il genitore può passare title e onClose come props. La modal potrebbe possedere isAnimating come state.
Anche se stai generando UI tramite chat (per esempio su Koder.ai), questa separazione è ancora il modo più veloce per restare sane: decidi prima cosa è props e cosa è state, poi lascia che la UI segua.
Un modo utile per tenere React in testa (molto nello spirito di Dan Abramov) è: il rendering è un calcolo, non un lavoro di pittura. React esegue le tue funzioni di componente per capire come dovrebbe apparire l'interfaccia per le props e lo state correnti. L'output è una descrizione della UI, non pixel.
Un re-render significa solo che React ripete quel calcolo. Non significa “l'intera pagina viene ridisegnata”. React confronta il nuovo risultato con il precedente e applica il minimo insieme di cambiamenti al DOM reale. Molti componenti possono re-renderizzare mentre solo pochi nodi DOM vengono effettivamente aggiornati.
La maggior parte dei re-render avviene per pochi motivi semplici: lo state di un componente è cambiato, le sue props sono cambiate, o un genitore ha re-renderizzato e React ha chiesto al figlio di renderizzare di nuovo. Quest'ultimo sorprende le persone, ma di solito va bene. Se tratti il render come “economico e noioso”, la tua app rimane più facile da ragionare.
La regola empirica che mantiene tutto pulito: rendi il render puro. Dati gli stessi input (props + state), il tuo componente dovrebbe restituire la stessa descrizione UI. Tieni le sorprese fuori dal render.
Esempio concreto: se generi un ID con Math.random() dentro il render, un re-render lo cambia e improvvisamente una checkbox perde il focus o un elemento di lista viene rimontato. Crea l'ID una sola volta (state, memo o fuori dal componente) e il render diventa stabile.
Se ricordi una frase: un re-render significa “ricalcola cosa dovrebbe essere l'interfaccia”, non “ricostruisci tutto”.
Un altro modello utile: gli aggiornamenti di stato sono richieste, non assegnazioni istantanee. Quando chiami un setter come setCount(count + 1), stai chiedendo a React di schedulare un render con un nuovo valore. Se leggi lo stato subito dopo, potresti vedere ancora il vecchio valore perché React non ha ancora renderizzato.
Per questo motivo gli aggiornamenti “piccoli e prevedibili” sono importanti. Preferisci descrivere il cambiamento invece di prendere quello che pensi sia il valore corrente. Quando il prossimo valore dipende da quello precedente, usa la forma updater: setCount(c => c + 1). Corrisponde a come funziona React: più aggiornamenti possono essere messi in coda e poi applicati in ordine.
L'immutabilità è l'altra metà del quadro. Non cambiare oggetti e array in posto. Creane uno nuovo con la modifica. React può allora vedere “questo valore è nuovo”, e il tuo cervello può tracciare cosa è cambiato.
Esempio: togglare un todo. L'approccio sicuro è creare un nuovo array e un nuovo oggetto todo per l'elemento che hai cambiato. L'approccio rischioso è invertire todo.done = !todo.done dentro l'array esistente.
Mantieni anche lo stato minimo. Una trappola comune è memorizzare valori che puoi calcolare. Se hai già items e filter, non memorizzare filteredItems nello stato. Calcolalo durante il render. Pochi variabili di stato significa meno modi in cui i valori possono andare fuori sync.
Un test semplice per capire cosa appartiene allo stato:
Se stai costruendo UI via chat (incluso su Koder.ai), chiedi cambiamenti come patch piccole: “Aggiungi un booleano” o “Aggiorna questa lista in modo immutabile.” Cambiamenti piccoli ed espliciti mantengono il generatore e il tuo codice React allineati.
Il rendering descrive l'interfaccia. Gli effetti sincronizzano con il mondo esterno. “Esterno” significa cose che React non controlla: chiamate di rete, timer, API del browser e talvolta lavoro DOM imperativo.
Se qualcosa può essere calcolato da props e state, di solito non dovrebbe vivere in un effect. Metterlo in un effect aggiunge un secondo passaggio (render, esegui effect, setta state, render di nuovo). Quel salto extra è dove compaiono flicker, loop e bug del tipo “perché questo è obsoleto?”.
Una confusione comune: hai firstName e lastName, e memorizzi fullName nello stato usando un effect. Ma fullName non è un effetto secondario. È dato derivato. Calcolalo durante il render e sarà sempre coerente.
Per abitudine: deriva i valori UI durante il render (o con useMemo quando è davvero costoso), e usa gli effect per “fare qualcosa”, non per “capire qualcosa”.
Tratta l'array delle dipendenze come: “Quando questi valori cambiano, risincronizza col mondo esterno.” Non è un trucco di performance e non è un posto per zittire i warning.
Esempio: se recuperi dettagli utente quando cambia userId, userId appartiene all'array delle dipendenze perché dovrebbe attivare la sincronizzazione. Se l'effect usa anche token, includilo, altrimenti potresti fare la fetch con un token vecchio.
Un buon controllo istintivo: se rimuovere un effect renderebbe solo sbagliata l'interfaccia, probabilmente non era un vero effect. Se rimuovendolo si interrompe un timer, si cancella una subscription, o si salta una fetch, probabilmente lo era.
Uno dei modelli mentali più utili è semplice: i dati scorrono verso il basso nell'albero, e le azioni utente risalgono.
Un genitore passa valori ai figli. I figli non dovrebbero “possedere” segretamente lo stesso valore in due posti. Richiedono cambiamenti chiamando una funzione, e il genitore decide quale sarà il nuovo valore.
Quando due parti dell'interfaccia devono essere d'accordo, scegli un posto unico per conservare il valore e poi passalo giù. Questo è il “lifting state”. Può sembrare tubature in più, ma previene un problema peggiore: due stati che divergono e ti costringono ad aggiungere hack per tenerli sincronizzati.
Esempio: una search box e una lista di risultati. Se l'input memorizza la sua query e la lista memorizza la sua query, alla fine vedrai “l'input mostra X ma la lista usa Y.” La soluzione è tenere query in un genitore, passarlo a entrambi e passare un handler onChangeQuery(newValue) all'input.
Sollevarlo non è sempre la risposta. Se un valore conta solo dentro un componente, tienilo lì. Tenere lo stato vicino al suo uso spesso rende il codice più leggibile.
Una barriera pratica:
Se non sei sicuro se sollevare lo stato, cerca segnali come: due componenti mostrano lo stesso valore in modi diversi; un'azione in un posto deve aggiornare qualcosa lontano; copi continuamente props nello stato “per sicurezza”; o aggiungi effect solo per mantenere due valori allineati.
Questo modello aiuta anche quando costruisci con strumenti di chat come Koder.ai: chiedi un unico proprietario per ogni pezzo di stato condiviso, poi genera handler che fluiscono verso l'alto.
Scegli una feature abbastanza piccola da tenere in testa. Una buona è una lista ricercabile dove puoi cliccare un elemento per vedere i dettagli in una modal.
Inizia schizzando le parti dell'interfaccia e gli eventi che possono succedere. Non pensare al codice ancora. Pensa a cosa può fare l'utente e cosa può vedere: c'è un input di ricerca, una lista, una riga selezionata evidenziata e una modal. Gli eventi sono digitare nella ricerca, cliccare un elemento, aprire la modal e chiudere la modal.
Ora “disegna lo stato”. Scrivi i pochi valori che devono essere conservati e decidi chi li possiede. Una regola semplice funziona: il genitore comune più vicino di tutti i posti che hanno bisogno di un valore dovrebbe possederlo.
Per questa feature, lo stato memorizzato può essere minimo: query (stringa), selectedId (id o null) e isModalOpen (booleano). La lista legge query e rende gli elementi. La modal legge selectedId per mostrare i dettagli. Se sia lista che modal hanno bisogno di selectedId, tienilo nel genitore, non in entrambi.
Poi separa i dati derivati da quelli memorizzati. La lista filtrata è derivata: filteredItems = items.filter(...). Non memorizzarla nello stato perché può sempre essere ricalcolata da items e query. Memorizzare dati derivati è il modo in cui i valori divergono.
Solo dopo chiediti: abbiamo bisogno di un effect? Se gli items sono già in memoria, no. Se digitare una query deve fare fetch, sì. Se chiudere la modal deve salvare qualcosa, sì. Gli effect servono per sincronizzare (fetch, save, subscribe), non per wiring UI di base.
Infine, testa il flusso con qualche caso limite:
selectedId è ancora valido?Se puoi rispondere a queste su carta, il codice React è di solito diretto.
La maggior parte della confusione in React non riguarda la sintassi. Succede quando il tuo codice smette di corrispondere alla semplice storia che hai in testa.
Memorizzare stato derivato. Salvi fullName nello stato anche se è solo firstName + lastName. Funziona finché un campo cambia e l'altro no, e l'interfaccia mostra un valore obsoleto.
Loop di effect. Un effect fa fetch, setta lo stato e la lista di dipendenze lo fa girare di nuovo. Il sintomo è richieste ripetute, UI tremolante o stato che non si stabilizza.
Closure obsoleta. Un handler di clic legge un valore vecchio (come un contatore o un filtro non aggiornato). Il sintomo è “ho cliccato, ma ha usato il valore di ieri”.
Stato globale ovunque. Mettere ogni dettaglio UI in uno store globale rende difficile capire chi possiede cosa. Il sintomo è che cambiando una cosa tre schermate reagiscono in modi sorprendenti.
Mutare oggetti annidati. Aggiorni un oggetto o un array in posto e ti chiedi perché l'interfaccia non si è aggiornata. Il sintomo è “i dati sono cambiati, ma niente ha re-renderizzato”.
Ecco un esempio concreto: un pannello “search and sort” per una lista. Se memorizzi filteredItems nello stato, può divergere da items quando arrivano nuovi dati. Invece, memorizza gli input (testo di ricerca, scelta sort) e calcola la lista filtrata durante il render.
Con gli effect, tienili per sincronizzarti col mondo esterno (fetching, subscription, timer). Se un effect sta facendo lavoro UI di base, spesso appartiene al render o a un handler di evento.
Quando generi o modifichi codice via chat, questi errori emergono più rapidamente perché le modifiche possono arrivare a blocchi. Una buona abitudine è inquadrare le richieste in termini di proprietà: “Qual è la fonte di verità per questo valore?” e “Possiamo calcolarlo invece di memorizzarlo?”.
Quando la tua UI inizia a sembrare imprevedibile, raramente è “troppo React.” Di solito è troppo stato, nei posti sbagliati, che fa lavori che non dovrebbe fare.
Prima di aggiungere un altro useState, fermati e chiediti:
Esempio piccolo: search box, dropdown filtro, lista. Se memorizzi sia query che filteredItems nello stato, ora hai due fonti di verità. Invece, tieni query e filter come stato, poi deriva filteredItems durante il render dall'intera lista.
Questo conta anche quando costruisci rapidamente con strumenti chat. La velocità è fantastica, ma continua a chiedere: “Abbiamo aggiunto stato, o abbiamo aggiunto un valore derivato per errore?” Se è derivato, elimina quello stato e calcolalo.
Un piccolo team costruisce una UI admin: una tabella di ordini, qualche filtro e un dialog per modificare un ordine. La prima richiesta è vaga: “Aggiungi filtri e un popup di modifica.” Sembra semplice, ma spesso si trasforma in stato sparso dappertutto.
Rendilo concreto traducendo la richiesta in stato ed eventi. Invece di “filtri”, nomina lo stato: query, status, dateRange. Invece di “popup di modifica”, nomina l'evento: “l'utente clicca Edit su una riga.” Poi decidi chi possiede ogni pezzo di stato (pagina, tabella o dialog) e cosa può essere derivato (come la lista filtrata).
Esempi di prompt che mantengono intatto il modello (funzionano bene anche in builder chat come Koder.ai):
OrdersPage che possiede filters e selectedOrderId. OrdersTable è controllata da filters e chiama onEdit(orderId).”visibleOrders da orders e filters. Non memorizzare visibleOrders nello stato.”EditOrderDialog che riceve order e open. Quando salvi, chiama onSave(updatedOrder) e chiudi.”filters con l'URL, non per calcolare le righe filtrate.”Dopo che l'interfaccia è generata o aggiornata, rivedi i cambiamenti con un controllo rapido: ogni valore di stato ha un solo proprietario, i valori derivati non sono salvati, gli effect servono solo per sincronizzare con il mondo esterno (URL, network, storage), e gli eventi fluiscono giù come props e su come callback.
Quando lo stato è prevedibile, iterare diventa sicuro. Puoi cambiare il layout della tabella, aggiungere un nuovo filtro o modificare i campi del dialog senza indovinare quale stato nascosto si romperà dopo.
La velocità è utile solo se l'app resta facile da ragionare. La protezione più semplice è trattare questi modelli mentali come una checklist da applicare prima di scrivere (o generare) UI.
Inizia ogni feature allo stesso modo: scrivi lo stato necessario, gli eventi che possono cambiarlo e chi lo possiede. Se non riesci a dire, “Questo componente possiede questo stato, e questi eventi lo aggiornano,” probabilmente finirai con stato sparso e re-render sorprendenti.
Se costruisci tramite chat, inizia in modalità pianificazione. Descrivi i componenti, la forma dello stato e le transizioni in linguaggio semplice prima di chiedere codice. Per esempio: “Un pannello filtri aggiorna lo stato query; la lista dei risultati è derivata da query; selezionare un elemento imposta selectedId; chiudere pulisce la selezione.” Quando questo suona chiaro, generare la UI diventa un passo meccanico.
Se usi Koder.ai (Koder.ai) per generare codice React, vale la pena fare un rapido controllo di sanità prima di procedere: un proprietario chiaro per ogni valore di stato, UI derivata dallo stato, effect solo per sincronizzare, e nessuna fonte duplicata di verità.
Poi itera a piccoli passi. Se vuoi cambiare la struttura dello stato (per esempio da diversi booleani a un singolo campo status), fai prima uno snapshot, sperimenta e torna indietro se il modello mentale peggiora. E quando serve una revisione più profonda o un handoff, esportare il codice sorgente rende più facile rispondere alla domanda reale: la forma dello stato racconta ancora la storia dell'interfaccia?
Un buon modello di partenza è: UI = f(state, props). I tuoi componenti non "modificano il DOM"; descrivono cosa dovrebbe esserci sullo schermo per i dati attuali. Se lo schermo è sbagliato, guarda lo state/props che l'hanno prodotto, non il DOM.
Le props sono input da un genitore; il tuo componente dovrebbe considerarle in sola lettura. Lo state è memoria posseduta da un componente (o dal componente che scegli come proprietario). Se un valore deve essere condiviso, sollevalo verso l'alto e passalo come props.
Un re-render significa che React riesegue la funzione del tuo componente per calcolare la prossima descrizione UI. Non significa automaticamente che l'intera pagina viene ridipinta. React poi aggiorna il DOM reale con il minimo insieme di modifiche necessario.
Perché gli aggiornamenti di stato sono programmati, non assegnazioni immediate. Se il prossimo valore dipende da quello precedente, usa la forma updater così non fai affidamento su un valore potenzialmente obsoleto:
setCount(c => c + 1)Così rimane corretto anche se più aggiornamenti sono in coda.
Evita di salvare tutto ciò che puoi calcolare da input esistenti. Conserva gli input e deriva il resto durante il render.
Esempi:
items, filtervisibleItems = items.filter(...)Questo evita che i valori si sfasino.
Usa gli effect per sincronizzarti con cose che React non controlla: fetch, subscription, timer, API del browser o lavoro DOM imperativo.
Non usare un effect solo per calcolare valori UI dallo state—calcola quelli durante il render (o con useMemo se è costoso).
Tratta le dipendenze come una lista di trigger: “quando questi valori cambiano, risincronizza”. Includi ogni valore reattivo che l'effect legge.
Se lasci fuori qualcosa rischi dati obsoleti (es. un userId o token vecchio). Se aggiungi cose sbagliate puoi creare loop—spesso segno che l'effect sta facendo lavoro che appartiene ad eventi o al render.
Se due parti dell'interfaccia devono sempre essere allineate, metti lo state nel closest common parent, passalo verso il basso e fornisci callback verso l'alto.
Un test rapido: se duplichi lo stesso valore in due componenti e scrivi effect per tenerli sincronizzati, quello stato probabilmente ha bisogno di un unico proprietario.
Succede quando un handler “cattura” un vecchio valore da un render precedente. Fix comuni:
setX(prev => ...)Se un click usa “il valore di ieri”, sospetta una closure obsoleta.
Comincia con un piccolo piano: componenti, proprietari di stato ed eventi. Poi genera codice come piccole patch (aggiungi un campo di stato, un handler, deriva un valore) invece di riscritture grandi.
Se usi un builder chat come Koder.ai, chiedi per:
Questo mantiene il codice generato allineato al modello mentale di React.