Lezione 2 – Web App con react-vite-nodejs-componenti JSX

Serie · Sviluppo Web Lezione 2 di 10 ⏱ ~20 min lettura Livello: Principiante

Nella lezione precedente hai installato Node.js, configurato VS Code e creato il primo progetto Vite + React. Il dev server gira, la pagina di default si aggiorna istantaneamente. Ora viene la parte interessante: capire come funziona davvero React e scrivere i primi componenti personalizzati per il progetto DevNotes.

In questa lezione affrontiamo i due concetti fondamentali di React: JSX e i componenti. Non sono complicati, ma è cruciale capirli bene — tutto il resto di React si costruisce sopra questi due mattoni.

// cosa costruiremo in questa lezione

Organizziamo il progetto DevNotes in una struttura professionale, sostituiamo il codice di default con i primi componenti reali: Header, NoteCard e NoteList. Impariamo a passare dati tra componenti con le props e capiamo la differenza tra export default e named export.

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) trasforma in chiamate JavaScript standard prima di servire il file al browser.

Concretamente, questo JSX:

const elemento = (
  <div className="card">
    <h2>Titolo nota</h2>
    <p>Contenuto della nota</p>
  </div>
)

viene trasformato da Vite in questo JavaScript puro:

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 formale

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.

Le differenze tra JSX e HTML

JSX assomiglia a HTML ma ha regole diverse. Conoscerle evita ore di debug su errori apparentemente misteriosi.

RegolaHTMLJSXMotivo
Attributo classeclass="card"className="card"class è una parola riservata in JavaScript
Attributo for (label)for="email"htmlFor="email"for è una 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 radice— (più tag fratelli OK)Necessario un wrapper o <></>JSX ritorna un singolo valore JavaScript
Espressioni JavaScriptNon supportate{ espressione }Le graffe eseguono codice JS dentro il markup
Commenti<!-- commento -->{/* commento */}I commenti in JSX sono espressioni JS

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.

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>
)
// attenzione: le graffe accettano espressioni, non statement

Dentro le graffe puoi scrivere 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.

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 dei dati in input (props) e restituisce JSX in output.

// definizione formale

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 dei componenti di DevNotes
App
← componente radice
Header
logo + navigazione
NoteList
griglia di note
NoteCard
singola nota (ripetuta per ogni nota)

Il primo componente — anatomia completa

Ecco la struttura minima di un componente React funzionale, con ogni parte spiegata:

// 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 destrutto

  // 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

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

// flusso props genitore → figlio
GENITORE — NoteList
<NoteCard
  titolo="Setup ambiente"        ← prop stringa
  tag="React"
  completata={true}              ← prop booleana (graffe!)
  onElimina={handleElimina}    ← prop funzione (callback)
/>
FIGLIO — NoteCard riceve le props
function NoteCard({ titolo, tag, completata, onElimina }) {
  // titolo → "Setup ambiente"
  // tag → "React"
  // completata → true
  // onElimina → la funzione del genitore
  return (...{titolo}...)
}
// destrutturazione vs oggetto props

Puoi ricevere le props in due modi: come oggetto intero function NoteCard(props) e poi usarle come props.titolo, oppure destrutturandole direttamente nel parametro function NoteCard({ titolo, tag }). La seconda forma è quasi sempre preferita: è più concisa e rende evidente a colpo d’occhio quali props usa il componente.

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.

// 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>

Export default vs named export

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.

EXPORT DEFAULT
// NoteCard.jsx
function NoteCard() { ... }
export default NoteCard

// App.jsx — import
import NoteCard from './NoteCard'
// oppure qualunque nome!
import Scheda from './NoteCard'

Un solo default export per file. L’import può usare qualunque nome (anche diverso dall’originale). Usato per il componente principale del file.

NAMED EXPORT
// utils.js
export function formattaData(d) { ... }
export const COLORI = ['blue', 'red']

// App.jsx — import
import { formattaData, COLORI }
  from './utils'
// il nome DEVE corrispondere (salvo alias)

Più named export per file. Il nome nell’import deve corrispondere esattamente (o usare alias con as). Usato per utility, costanti, hook multipli.

// convenzione del settore

Un file di componente React (.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

Vite genera tutto in src/ senza sotto-cartelle. Va bene per un componente, ma per un progetto reale con 10-20 componenti diventa rapidamente un caos. Adottiamo subito una struttura professionale:

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 file CSS Modules. Questo pattern (chiamato spesso “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.

⚗️
LAB PRATICO — LEZIONE 2

Il file lab-02-componenti-jsx.md ti guida passo-passo nel riorganizzare il progetto DevNotes, creare i componenti Header, NoteCard e NoteList con dati statici, e collegare tutto in App.jsx.

Apri su GitHub →
📌 Riepilogo — Cosa abbiamo visto
  • JSX è sintassi estesa di JavaScript, non HTML: viene compilato in React.createElement() da Vite. Le principali differenze: className, attributi camelCase, tag auto-chiudenti, un solo elemento radice.
  • Le graffe { } in JSX aprono una finestra su JavaScript: accettano espressioni (variabili, ternari, .map()), non statement (if, for).
  • Un componente è una funzione con nome maiuscolo che riceve props e restituisce JSX. La composizione di componenti semplici costruisce interfacce complesse.
  • Le props fluiscono sempre dal genitore al figlio (unidirezionale). Vanno destrutto nel parametro della funzione. La prop speciale children contiene il contenuto annidato.
  • export default per il componente principale del file (uno per file), named export per utility e costanti (più per file).
  • La prossima lezione introduce useState e useEffect: rendiamo l’interfaccia interattiva — le note si aggiungono, si eliminano, si filtrano.

Lascia un commento