Lezione 2 — Componenti e JSX: i mattoni di React
Nella L01 il dev server gira e la pagina di default si aggiorna. Ora capiamo come funziona davvero React: cos'è JSX, come si scrive un componente funzionale, come si passano dati con le props e come organizzare il progetto DevNotes in modo professionale.
1. Cos'è JSX — e perché esiste
Quando apri src/App.jsx vedrai qualcosa di strano: codice JavaScript che contiene tag tipo HTML, senza virgolette, in mezzo a funzioni normali. Questo è JSX — JavaScript XML.
JSX non è HTML. Non lo capisce il browser. Non lo capisce neanche Node.js da solo. È una sintassi estesa di JavaScript che Vite (tramite il plugin @vitejs/plugin-react che usa Babel/esbuild) trasforma in chiamate JavaScript standard prima di servire il file al browser.
Il secondo è equivalente al primo, ma è illeggibile per un essere umano. JSX esiste proprio per questo: rende il codice dell'interfaccia leggibile e manutenibile, senza sacrificare la potenza di JavaScript.
JSX è un'estensione sintattica di JavaScript che permette di scrivere strutture simili all'HTML direttamente nel codice JS. Non è un linguaggio separato: viene compilato in chiamate React.createElement() da strumenti come Babel o esbuild (usato internamente da Vite). Il risultato è un React Element: un oggetto JavaScript che descrive cosa rendere nel DOM.
Quando leggi JSX, pensa sempre: "questo è zucchero sintattico". Ogni tag che vedi è in realtà una funzione. Se ti chiedi cosa fa davvero <Header />, la risposta è: chiama React.createElement(Header). Questo ti aiuta a capire perché i componenti custom DEVONO iniziare con la maiuscola: <div> diventa stringa 'div', <Header> diventa variabile Header.
2. Differenze tra JSX e HTML — tabella completa
JSX assomiglia a HTML ma ha regole diverse. Conoscerle evita ore di debug su errori apparentemente misteriosi.
| Regola | HTML | JSX | Motivo |
|---|---|---|---|
| Attributo classe | class="card" | className="card" | class è parola riservata in JavaScript |
| Attributo for (label) | for="email" | htmlFor="email" | for è parola riservata in JavaScript |
| Tag auto-chiudenti | <input> <img> <br> | <input /> <img /> <br /> | In JSX tutti i tag devono essere chiusi esplicitamente |
| Attributi camelCase | onclick tabindex | onClick tabIndex | JSX usa convenzioni JavaScript, non HTML |
| Stili inline | style="color: red" | style={{color: 'red'}} | Lo stile è un oggetto JS, non una stringa |
| Un solo elemento radice | Più tag fratelli OK | Necessario wrapper o <></> | JSX ritorna un singolo valore JS |
| Espressioni JavaScript | Non supportate | { espressione } | Le graffe eseguono codice JS dentro il markup |
| Commenti | <!-- commento --> | {/* commento */} | I commenti in JSX sono espressioni JS |
Dimenticare il tag wrapper. Se return contiene due <div> fratelli senza genitore, Vite ti mostra "Adjacent JSX elements must be wrapped". Soluzione: avvolgi tutto in un <div> o usa il Fragment vuoto <> ... </> . L'ho visto succedere almeno tre volte per classe in ogni corso.
3. Le graffe { } — il superpotere di JSX
Le graffe dentro JSX aprono una "finestra" su JavaScript: puoi scriverci qualunque espressione valida. Questo è ciò che rende le interfacce React dinamiche.
Dentro le graffe puoi scrivere solo espressioni (valori che producono un risultato): variabili, operatori ternari, chiamate a funzioni, .map(). Non puoi scrivere statement: niente if, for, while, const. Se hai bisogno di logica complessa, mettila fuori dal return, prima del JSX, oppure usa funzioni helper.
Per if/else brevi usa il ternario {condizione ? <ComponenteA /> : <ComponenteB />}. Per "mostra solo se vero" usa && : {tags.length > 0 && <Lista />}. Se la logica supera 2 righe, estraila in una const prima del return: leggibilità prima di tutto.
4. I componenti — gli atomi dell'interfaccia
React è costruito sul concetto di componente: un'unità autonoma e riusabile dell'interfaccia utente. Ogni componente è una funzione JavaScript che riceve dati in input (props) e restituisce JSX in output.
Un componente React è una funzione JavaScript il cui nome inizia con la lettera maiuscola, che accetta un unico argomento (props) e restituisce React Elements (JSX). Il nome maiuscolo è fondamentale: React distingue i tag HTML standard (<div>, minuscolo) dai componenti personalizzati (<NoteCard>, maiuscolo) proprio dalla capitalizzazione.
L'idea chiave è la composizione: costruisci interfacce complesse assemblandole da componenti semplici, esattamente come costruisci una casa con i mattoni. Ogni mattone è indipendente, testabile da solo, e riutilizzabile in contesti diversi.
Albero componenti DevNotes — App contiene Header e NoteList, che a sua volta renderizza NoteCard
Il primo componente — anatomia completa
Ecco la struttura minima di un componente React funzionale, con ogni parte spiegata:
Dipendenze
CSS Modules, altri componenti, utility. Tutto in alto, esplicito.
JS puro
Variabili, formattazioni, handler. Fuori dal return, prima del JSX.
Markup + graffe
Un solo elemento radice, interpolazioni con { } , key nelle liste.
5. Le Props — come i componenti comunicano
Le props (abbreviazione di properties) sono il meccanismo con cui un componente genitore passa dati a un componente figlio. Il flusso è sempre unidirezionale: dall'alto verso il basso nell'albero dei componenti. Un figlio non può modificare le props che riceve — può solo leggerle.
Concettualmente, le props sono identiche ai parametri di una funzione. Quando scrivi <NoteCard titolo="Setup" /> è come chiamare NoteCard({ titolo: "Setup" }).
Se <NoteCard titolo="Setup" /> vi sembra magia, riscrivetelo come chiamata funzione: NoteCard({titolo: "Setup"}). La sintassi JSX è solo zucchero sintattico sopra un oggetto. Questo spiega perché le props non si modificano nel figlio: sono come parametri, in sola lettura.
Puoi ricevere props come oggetto intero function NoteCard(props) e poi usare props.titolo, ma è verboso. Destruttura subito nel parametro: function NoteCard({ titolo, tag }). È la forma usata nel 99% dei progetti reali, rende evidente a colpo d'occhio cosa usa il componente e viene apprezzata nelle code review.
La prop speciale: children
Esiste una prop speciale chiamata children che contiene tutto ciò che viene scritto tra i tag di apertura e chiusura del componente. È fondamentale per costruire componenti contenitore generici come card, modal o layout.
Le props sono immutabili per convenzione. Se nel figlio scrivi props.titolo = "nuovo", stai violando il flusso unidirezionale e React non ri-renderizzerà correttamente. Se devi modificare un dato passato dal genitore, il genitore deve passare una callback (es. onModifica) che il figlio chiama per chiedere l'aggiornamento.
6. Export default vs named export + organizzazione progetto
Ogni file di componente deve esportare ciò che contiene per renderlo importabile. Esistono due modi, e capire la differenza evita errori di import che possono confondere all'inizio.
| Tipo export | Sintassi | Regola import | Quando usarlo |
|---|---|---|---|
| export default | export default NoteCard | import NoteCard from './NoteCard' — nome libero, anche diverso | Un solo default per file — il componente principale del file |
| named export | export function formattaData(){} export const COLORI = [] | import { formattaData, COLORI } from './utils' — nome DEVE corrispondere | Più export per file — utility, costanti, hook multipli |
| Alias | export { X as Y } | import { COLORI as Colori } | Rinomina in import per evitare conflitti |
Un file .jsx contiene quasi sempre un solo componente esportato come export default. I file di utility (utils.js), costanti e hook personalizzati usano invece named export perché possono esportare più cose dallo stesso file. Segui questa convenzione e il tuo codice sarà immediatamente comprensibile a qualunque sviluppatore React.
Organizzare il progetto in cartelle — struttura professionale DevNotes
Vite genera tutto in src/ senza sotto-cartelle. Va bene per un componente, ma con 10-20 componenti diventa un caos. Adottiamo subito una struttura professionale:
Ogni componente ha la sua sotto-cartella con il file JSX e il relativo CSS Modules. Questo pattern (chiamato "component colocation") tiene insieme tutto ciò che riguarda un componente: il markup, lo stile, eventualmente i test. Se elimini la cartella, elimini il componente senza lasciare file CSS orfani in giro.
All'inizio sembra eccessivo creare una cartella per ogni componente. Dopo 3 lezioni, quando avrai Header, NoteCard, NoteList, NoteForm, SearchBar, capirai: trovare al volo NoteCard.module.css nella sua cartella è immediato. Tenere tutto in src/ con 15 file mischiati diventa ingestibile.
7. Verifica / Esercizio pratico
🔬 Attività — Lab 02
Apri il lab pratico dal meta-pill giallo in alto. Riorganizza il progetto DevNotes e rispondi:
- Crea
components/Header/Header.jsxche mostra titolo DevNotes e un sottotitolo. Importalo in App.jsx. Cosa succede se scrivi il componente comefunction header()minuscolo? - In
NoteCard, aggiungi una propcompletatabooleana e mostra un badge diverso con rendering condizionale ternario. Come gestisci valori mancanti? - Crea
components/NoteList/NoteList.jsxche riceve un array di 3 note statiche e le mappa con.map(). Quale attributo è obbligatorio nella lista e perché? - Prova a scrivere due tag fratelli nel return senza wrapper: quale errore vedi? Correggilo con Fragment
<></>.
1. Con header() minuscolo, React lo tratta come tag HTML <header> nativo, non come componente: non verrà chiamata la funzione. Usa sempre PascalCase. 2. Default value nella destrutturazione: function NoteCard({ completata = false }). Poi {completata ? '✅ Completa' : '⏳ Da fare'}. 3. Attributo key unico per ogni elemento della lista, serve a React per il diffing del Virtual DOM. 4. Errore "Adjacent JSX elements" — il return JSX deve restituire un solo elemento radice.
8. Riepilogo e concetti chiave
📚 Prossimo passo — L03 Stato e interattività
Ora i tuoi componenti mostrano dati statici. Nella prossima lezione li rendiamo vivi: useState per il contatore e la lista, eventi onClick, useEffect per il ciclo di vita. Porta con te Header, NoteCard e NoteList funzionanti.
