L02 - Componenti e JSX | React + Vite · DevNotes - Prof. Giagnotti
PROF GIAGNOTTI · TORNA A → · GUIDE
📘 GUIDE · React + Vite · Lezione 2 di 10
Docente: Prof. Giagnotti · Sviluppo Web Moderno

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.

⏱ ~20 min lettura 🎯 Livello: Principiante 💻 Prerequisiti: L01 completata, progetto Vite attivo ⚗️ Lab Pratico — lab-02-componenti-jsx.md ↗ 🧩 Serie: DevNotes
✓Capire cos'è JSX, perché esiste e come Vite lo trasforma in React.createElement()
✓Conoscere le differenze cruciali tra HTML e JSX (className, tag chiusi, graffe)
✓Scrivere componenti funzionali con nome maiuscolo, props e composition
✓Passare dati con props, usare destrutturazione e la prop speciale children
✓Distinguere export default e named export e organizzare src/ in cartelle professionali

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.

JSX · Input che scrivi
const elemento = ( <div className="card"> <h2>Titolo nota</h2> <p>Contenuto della nota</p> </div> )
JS · Output compilato da Vite
const elemento = React.createElement( 'div', { className: 'card' }, React.createElement('h2', null, 'Titolo nota'), React.createElement('p', null, 'Contenuto della nota') )

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.

📌 Definizione da ricordare — JSX

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.

💬 Tip del docente

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.

RegolaHTMLJSXMotivo
Attributo classeclass="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 camelCaseonclick tabindexonClick tabIndexJSX usa convenzioni JavaScript, non HTML
Stili inlinestyle="color: red"style={{color: 'red'}}Lo stile è un oggetto JS, non una stringa
Un solo elemento radicePiù tag fratelli OKNecessario wrapper o <></>JSX ritorna un singolo valore JS
Espressioni JavaScriptNon supportate{ espressione }Le graffe eseguono codice JS dentro il markup
Commenti<!-- commento -->{/* commento */}I commenti in JSX sono espressioni JS
⚠️ Errore comune visto in laboratorio

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.

JSX · Graffe e dinamica
const titolo = 'Setup ambiente di sviluppo' const data = new Date().toLocaleDateString('it-IT') const tags = ['React', 'Node.js', 'Vite']return ( <div> {/* variabile stringa */} <h2>{titolo}</h2>{/* espressione */} <p>Creata il {data}</p>{/* ternario: rendering condizionale */} <p>{tags.length > 0 ? 'Ha tag' : 'Nessun tag'}</p>{/* .map() per generare liste di elementi */} <ul> {tags.map(tag => <li key={tag}>{tag}</li>)} </ul> </div> )
📌 Regola d'oro delle graffe

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.

💬 Tip del docente — quando usare il ternario

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.

📌 Definizione formale — Componente React

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.

App ← radice Header NoteList NoteCard griglia di note ripetuta per ogni nota

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:

JSX · NoteCard.jsx — anatomia
// 1. Import: tutto ciò che il componente usa dall'esterno import styles from './NoteCard.module.css'// 2. La funzione componente (nome con lettera MAIUSCOLA) function NoteCard({ titolo, contenuto, tag }) { // ← props destrutturato// 3. Logica JavaScript (variabili, calcoli, handler) // Tutto ciò che non è JSX va QUI, prima del return const dataFormattata = new Date().toLocaleDateString('it-IT')// 4. Il return: il JSX che verrà renderizzato nel DOM return ( <article className={styles.card}> <span className={styles.tag}>{tag}</span> <h3 className={styles.titolo}>{titolo}</h3> <p className={styles.contenuto}>{contenuto}</p> <time className={styles.data}>{dataFormattata}</time> </article> ) }// 5. Export: rende il componente importabile da altri file export default NoteCard
01 · IMPORT

Dipendenze

CSS Modules, altri componenti, utility. Tutto in alto, esplicito.

02 · LOGICA

JS puro

Variabili, formattazioni, handler. Fuori dal return, prima del JSX.

03 · RETURN 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" }).

JSX · Flusso props genitore → figlio
// GENITORE — NoteList.jsx <NoteCard titolo="Setup ambiente" // ← prop stringa tag="React" completata={true} // ← prop booleana (graffe!) onElimina={handleElimina} // ← prop funzione (callback) />// FIGLIO — NoteCard.jsx riceve le props function NoteCard({ titolo, tag, completata, onElimina }) { // titolo → "Setup ambiente" // tag → "React" // completata → true // onElimina → la funzione del genitore return (...{titolo}...) }
💡 Esempio in classe — props come parametri

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.

💬 Tip del docente — destrutturazione

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.

JSX · children prop
// Definizione del componente contenitore function Card({ children, titolo }) { return ( <div className="card"> <h3>{titolo}</h3> <div className="card-body">{children}</div> </div> ) }// Utilizzo: ciò che è "dentro" il tag diventa children <Card titolo="La mia nota"> <p>Questo testo è children</p> <button>Anche questo bottone è children</button> </Card>
⚠️ Errore comune — mutare le props

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 exportSintassiRegola importQuando usarlo
export defaultexport default NoteCardimport NoteCard from './NoteCard' — nome libero, anche diversoUn solo default per file — il componente principale del file
named exportexport function formattaData(){}
export const COLORI = []
import { formattaData, COLORI } from './utils' — nome DEVE corrisponderePiù export per file — utility, costanti, hook multipli
Aliasexport { X as Y }import { COLORI as Colori }Rinomina in import per evitare conflitti
JS · Confronto import/export
// NoteCard.jsx — export default function NoteCard() { ... } export default NoteCard// App.jsx — import default (nome libero!) import NoteCard from './NoteCard' import Scheda from './NoteCard' // valido!// utils.js — named export export function formattaData(d) { return ... } export const COLORI = ['blue', 'red']// App.jsx — import named (nome obbligatorio) import { formattaData, COLORI } from './utils' import { formattaData as fmt } from './utils' // alias
💬 Tip del docente — convenzione del settore

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:

STRUTTURA · src/
src/ ├── components/ ← componenti riutilizzabili (UI pura) │ ├── Header/ │ │ ├── Header.jsx │ │ └── Header.module.css │ ├── NoteCard/ │ │ ├── NoteCard.jsx │ │ └── NoteCard.module.css │ └── NoteList/ │ ├── NoteList.jsx │ └── NoteList.module.css ├── pages/ ← componenti che rappresentano pagine (una per route) │ └── Home.jsx ├── utils/ ← funzioni di utilità pure (no JSX) │ └── formatters.js ├── App.jsx ├── index.css └── main.jsx

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.

🎯 Esempio in classe — perché così

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:

  1. Crea components/Header/Header.jsx che mostra titolo DevNotes e un sottotitolo. Importalo in App.jsx. Cosa succede se scrivi il componente come function header() minuscolo?
  2. In NoteCard, aggiungi una prop completata booleana e mostra un badge diverso con rendering condizionale ternario. Come gestisci valori mancanti?
  3. Crea components/NoteList/NoteList.jsx che riceve un array di 3 note statiche e le mappa con .map(). Quale attributo è obbligatorio nella lista e perché?
  4. Prova a scrivere due tag fratelli nel return senza wrapper: quale errore vedi? Correggilo con Fragment <></>.
✅ Soluzione ragionata

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.

✓JSX è sintassi estesa JS compilata da Vite in React.createElement(): leggibile per noi, eseguibile per il browser.
✓Differenze HTML vs JSX: className, htmlFor, tag chiusi />, camelCase (onClick), style oggetto, Fragment <>.
✓Graffe { }: finestra su JavaScript dentro JSX — solo espressioni (ternari, map, variabili), non statement.
✓Componente = funzione PascalCase che riceve props e ritorna JSX. Composizione = App → Header + NoteList → NoteCard.
✓Props unidirezionali genitore→figlio, immutabili nel figlio. children = contenuto annidato tra tag.
✓Export default (uno per file, nome libero) per componenti, named export (più per file, nome esatto) per utility.

📚 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.

Lascia un commento