Componenti, Props e State in React

8 dicembre 2025
14 min di lettura

Introduzione

I componenti sono funzioni JavaScript che restituiscono JSX. Le props permettono di passare dati dai componenti padre ai figli. Lo state gestisce dati che cambiano nel tempo e aggiorna automaticamente l’interfaccia quando cambiano.

Componenti: I Blocchi Fondamentali di React

I componenti sono funzioni JavaScript che restituiscono codice JSX da renderizzare sullo schermo. Ogni componente incapsula markup HTML, stili CSS e logica JavaScript correlata, permettendo di organizzare il codice in modo modulare e riutilizzabile.

Ogni parte visibile di un’interfaccia può essere identificata come un componente potenziale: un header, una card, un pulsante, una lista.

Creare il Primo Componente

Un componente React è semplicemente una funzione JavaScript che segue due regole fondamentali:

  1. Il nome della funzione deve iniziare con una lettera maiuscola
  2. La funzione deve restituire un valore renderizzabile (tipicamente codice JSX)
// Esempio di componente semplice
function Header() {
return (
<header>
<img src="react-logo.png" alt="React logo" />
<h1>Concetti Fondamentali di React</h1>
<p>I blocchi di costruzione essenziali</p>
</header>
);
}
Perché il nome deve iniziare con maiuscola?

React distingue tra componenti incorporati (come div, img, h1) e componenti personalizzati attraverso la convenzione di naming: elementi HTML con minuscola, componenti personalizzati con maiuscola. Gli elementi incorporati vengono renderizzati come nodi DOM reali, mentre i componenti personalizzati sono funzioni che vengono eseguite da React.

Utilizzare i Componenti

Per utilizzare un componente, non lo si chiama come una normale funzione JavaScript. Invece, si usa come un elemento HTML all’interno del codice JSX:

function App() {
return (
<div id="app">
{/* Usare il componente come elemento HTML */}
<Header />
{/* Oppure con tag di apertura e chiusura */}
<Header></Header>
</div>
);
}
Sintassi di autochiusura

Quando si usa un componente senza contenuto interno, si usa la sintassi di autochiusura <ComponentName />. La barra / finale è obbligatoria per i componenti personalizzati.

JSX: JavaScript Syntax Extension

JSX è un’estensione della sintassi JavaScript che permette di scrivere codice HTML-like direttamente nei file JavaScript. Non è JavaScript standard e non è supportato nativamente dai browser, ma viene trasformato in codice JavaScript valido durante il processo di build.

// Questo è JSX - sembra HTML ma è dentro JavaScript
function App() {
return (
<div>
<h1>Ciao Mondo</h1>
<p>Questo è JSX</p>
</div>
);
}
Estensioni di file: .jsx vs .js

L’estensione .jsx indica che un file contiene codice JSX, ma non è sempre obbligatoria. Dipende dalla configurazione del processo di build. L’estensione serve principalmente al processo di build, non al browser.

Valori Dinamici con le Parentesi Graffe

Una delle caratteristiche più potenti di JSX è la possibilità di inserire valori dinamici usando le parentesi graffe {}. Qualsiasi espressione JavaScript inserita tra parentesi graffe verrà valutata e il risultato verrà visualizzato.

function Header() {
// Array di possibili descrizioni
const reactDescriptions = ['Fundamental', 'Crucial', 'Core'];
// Funzione per generare un indice casuale
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
// Generare una descrizione casuale
const description = reactDescriptions[getRandomInt(3)];
return (
<header>
<h1>{description} React Concepts</h1>
{/* Le parentesi graffe permettono di inserire JavaScript */}
<p>Numero casuale: {Math.random()}</p>
<p>Calcolo: {1 + 1}</p>
</header>
);
}
Dove si possono usare le parentesi graffe

Le parentesi graffe possono essere usate in diversi contesti:

  1. Tra i tag: per inserire contenuto dinamico

    <h1>{title}</h1>
  2. Come valore di attributo: per attributi dinamici

    <img src={imagePath} alt={imageAlt} />
  3. Per valori booleani: alcuni attributi accettano booleani

    <button disabled={isLoading}>Invia</button>

Quando si usa una parentesi graffa per un valore di attributo, non si aggiungono le virgolette. Il valore dinamico va direttamente dopo il segno =.

// ✅ Corretto
<img src={imagePath} />
// ❌ Errato - non servono le virgolette
<img src="{imagePath}" />

Importare Immagini in React

Per includere immagini in un progetto React, è meglio usare le dichiarazioni di import invece di percorsi stringa relativi. Questo permette al processo di build di ottimizzare le immagini e includerle correttamente nel bundle finale.

// Importare l'immagine come modulo
import reactImg from './assets/react-core-concepts.png';
function Header() {
return (
<header>
{/* Usare la variabile importata come valore dinamico */}
<img src={reactImg} alt="React logo" />
</header>
);
}
Perché importare invece di usare percorsi stringa?

Quando si importa un’immagine, si ottiene una variabile che contiene il percorso ottimizzato dell’immagine. Il processo di build può ottimizzare le immagini e includerle correttamente nel bundle.

Props: Passare Dati ai Componenti

Le props (abbreviazione di “properties”) sono il meccanismo di React per passare dati da un componente padre a un componente figlio. Permettono di rendere i componenti riutilizzabili con dati diversi.

Definire e Utilizzare Props

Per passare dati a un componente, si aggiungono attributi personalizzati al componente stesso. Questi attributi diventano proprietà dell’oggetto props che React passa automaticamente alla funzione del componente.

// Componente che riceve props
function CoreConcept({ title, description, image }) {
return (
<li>
<img src={image} alt={title} />
<h3>{title}</h3>
<p>{description}</p>
</li>
);
}
// Utilizzare il componente con dati diversi
function App() {
return (
<ul>
<CoreConcept
title="Components"
description="I blocchi base dell'interfaccia utente"
image={componentsImg}
/>
<CoreConcept
title="JSX"
description="Sintassi estesa di JavaScript"
image={jsxImg}
/>
</ul>
);
}
Destrutturazione degli oggetti nelle props

Ci sono diversi modi per accedere alle props in un componente:

1. Accedere tramite l’oggetto props completo:

function CoreConcept(props) {
return (
<li>
<h3>{props.title}</h3>
<p>{props.description}</p>
</li>
);
}

2. Destrutturare direttamente nei parametri (consigliato):

function CoreConcept({ title, description, image }) {
return (
<li>
<h3>{title}</h3>
<p>{description}</p>
</li>
);
}

La destrutturazione è preferibile perché rende il codice più pulito e mostra chiaramente quali props il componente si aspetta.

Spread Operator per Props

Quando si hanno dati organizzati come oggetti JavaScript e i nomi delle proprietà corrispondono ai nomi delle props, si può usare l’operatore spread (...) per passare tutte le proprietà in una volta.

// Dati organizzati come oggetto
const conceptData = {
title: "Components",
description: "I blocchi base dell'interfaccia utente",
image: componentsImg
};
// Passare tutte le proprietà con spread
<CoreConcept {...conceptData} />
// Equivale a scrivere:
<CoreConcept
title={conceptData.title}
description={conceptData.description}
image={conceptData.image}
/>
Altri pattern per le props

Passare un singolo oggetto come prop:

// Passare l'intero oggetto come singola prop
<CoreConcept concept={conceptData} />
// Nel componente
function CoreConcept({ concept }) {
return (
<li>
<h3>{concept.title}</h3>
<p>{concept.description}</p>
</li>
);
}

Valori di default per le props:

function Button({ caption, type = "button" }) {
// type ha un valore di default "button"
// se non viene passato, userà questo valore
return <button type={type}>{caption}</button>;
}
// Usabile con o senza type
<Button caption="Clicca" /> // type sarà "button"
<Button caption="Invia" type="submit" /> // type sarà "submit"

La Prop Speciale children

La prop children è speciale perché non viene passata come attributo, ma rappresenta il contenuto inserito tra i tag di apertura e chiusura di un componente. Permette di creare componenti che avvolgono altri contenuti.

// Componente che usa children
function Card({ name, children }) {
return (
<article className="card">
<h2>{name}</h2>
{/* children contiene tutto ciò che è tra i tag */}
{children}
</article>
);
}
// Utilizzo del componente con contenuto interno
<Card name="Maria Miles">
<p>Maria è una professoressa di Computer Science.</p>
<p><a href="mailto:maria@example.com">Email Maria</a></p>
</Card>
Composizione di componenti

L’uso di children permette la composizione di componenti. Si può passare contenuto JSX complesso tra i tag di apertura e chiusura del componente.

Organizzare i Componenti in File Separati

Man mano che un’applicazione cresce, è importante organizzare i componenti in file separati per mantenere il codice gestibile e navigabile. La convenzione comune è creare una cartella components nella cartella src.

Struttura dei File

components/Header.jsx
import reactImg from '../assets/react-core-concepts.png';
export default function Header() {
return (
<header>
<img src={reactImg} alt="React logo" />
<h1>Concetti Fondamentali di React</h1>
</header>
);
}
// App.jsx
import Header from './components/Header';
function App() {
return (
<div id="app">
<Header />
</div>
);
}
Export default vs named export

Ci sono due modi per esportare componenti:

Export default (più comune per i componenti):

// Componente
export default function Header() { ... }
// Import
import Header from './components/Header';

Named export:

// Componente
export function Header() { ... }
// Import (con parentesi graffe)
import { Header } from './components/Header';

La maggior parte dei progetti React usa export default per i componenti perché permette di importare con qualsiasi nome e semplifica gli import quando si ha un componente principale per file.

Organizzare gli Stili CSS

Gli stili CSS possono essere organizzati in file separati, tipicamente uno per componente. Per includere gli stili, si importa il file CSS nel componente corrispondente.

components/Header.jsx
import './Header.css'; // Importare gli stili del componente
export default function Header() {
return <header>...</header>;
}
Limitazioni degli stili CSS standard

Importare CSS in un componente non limita gli stili a quel componente. Gli stili vengono applicati globalmente alla pagina. Per avere stili isolati per componente esistono soluzioni avanzate come CSS Modules o CSS-in-JS.

Gestione degli Eventi

Per rendere le applicazioni interattive, è necessario reagire agli eventi dell’utente (clic, input, hover, ecc.). In React, gli eventi vengono gestiti in modo dichiarativo usando props speciali che iniziano con on.

Aggiungere Event Listeners

Gli elementi HTML incorporati supportano props speciali per gli eventi, come onClick, onChange, onSubmit. Il valore di questi props deve essere una funzione che verrà eseguita quando l’evento si verifica.

function TabButton({ children, onSelect }) {
// Funzione che gestisce il click
function handleClick() {
console.log('Pulsante cliccato!');
}
return (
<li>
{/* Passare la funzione come valore, NON eseguirla */}
<button onClick={handleClick}>
{children}
</button>
</li>
);
}
Perché non eseguire la funzione direttamente?

È fondamentale capire la differenza tra:

// ✅ Corretto - passa la funzione come valore
<button onClick={handleClick}>Clicca</button>
// ❌ Errato - esegue la funzione immediatamente
<button onClick={handleClick()}>Clicca</button>

Quando si aggiungono le parentesi (), la funzione viene eseguita immediatamente quando il componente viene renderizzato, non quando l’utente clicca. Passando solo il nome della funzione (senza parentesi), si passa un riferimento alla funzione che React può chiamare al momento giusto.

Passare Parametri agli Event Handlers

Spesso si vuole passare dati personalizzati all’event handler quando viene chiamato. Per fare questo, si avvolge la chiamata della funzione in una funzione freccia anonima.

function App() {
function handleSelect(selectedButton) {
console.log('Selezionato:', selectedButton);
}
return (
<div>
{/* Passare un parametro personalizzato */}
<TabButton onSelect={() => handleSelect('components')}>
Components
</TabButton>
<TabButton onSelect={() => handleSelect('jsx')}>
JSX
</TabButton>
</div>
);
}
Pattern comune: forwarding di eventi

Un pattern molto comune è quello di “inoltrare” eventi da componenti personalizzati agli elementi HTML incorporati:

// Componente personalizzato che riceve una prop per l'evento
function TabButton({ children, onSelect }) {
return (
<li>
{/* Inoltrare la funzione all'elemento button incorporato */}
<button onClick={onSelect}>
{children}
</button>
</li>
);
}
// Nel componente padre
function App() {
function handleSelect() {
console.log('Tab selezionata');
}
return (
<TabButton onSelect={handleSelect}>
Components
</TabButton>
);
}

Questo pattern permette ai componenti personalizzati di essere controllati dall’esterno.

State: Gestire Dati che Cambiano

Lo state (stato) è uno dei concetti più importanti di React. Permette di gestire dati che cambiano nel tempo e di aggiornare automaticamente l’interfaccia utente quando questi dati cambiano.

Il Problema con le Variabili Normali

Se si prova a usare una variabile JavaScript normale per aggiornare l’interfaccia, si scopre che non funziona:

function App() {
let selectedTopic = 'components';
function handleSelect() {
selectedTopic = 'jsx'; // Aggiorna la variabile
console.log(selectedTopic); // Vede il nuovo valore
}
return (
<div>
<p>{selectedTopic}</p> {/* Non si aggiorna! */}
<button onClick={handleSelect}>Cambia</button>
</div>
);
}

React esegue la funzione del componente solo una volta quando viene incontrata per la prima volta. Aggiornare una variabile normale non dice a React di rieseguire la funzione e aggiornare l’interfaccia.

Usare useState Hook

La soluzione è usare lo useState Hook, che permette di registrare variabili gestite da React. Quando queste variabili cambiano, React riesegue automaticamente il componente e aggiorna l’interfaccia.

import { useState } from 'react';
function App() {
// useState restituisce un array con due elementi:
// [valore corrente, funzione per aggiornare il valore]
const [selectedTopic, setSelectedTopic] = useState('components');
function handleSelect() {
// Chiamare la funzione di aggiornamento
setSelectedTopic('jsx');
// React rieseguirà automaticamente il componente
}
return (
<div>
<p>{selectedTopic}</p> {/* Ora si aggiorna! */}
<button onClick={handleSelect}>Cambia</button>
</div>
);
}
Regole degli Hooks

Gli Hooks di React (come useState) devono seguire regole specifiche:

Gli Hooks devono essere chiamati solo all’interno di funzioni di componenti React o altri Hooks personalizzati, sempre al livello superiore (non dentro cicli, condizioni o funzioni annidate).

// ✅ Corretto
function App() {
const [count, setCount] = useState(0);
// ...
}
// ❌ Errato - dentro una condizione
function App() {
if (someCondition) {
const [count, setCount] = useState(0); // Errore!
}
}
// ❌ Errato - dentro una funzione annidata
function App() {
function handleClick() {
const [count, setCount] = useState(0); // Errore!
}
}

Queste regole garantiscono che React possa tracciare correttamente lo stato tra i render.

Come Funziona useState

useState accetta un argomento (il valore iniziale) e restituisce un array con esattamente due elementi:

  1. Il valore corrente dello stato: un’istantanea del valore per questo ciclo di render
  2. La funzione di aggiornamento: una funzione che aggiorna lo stato e dice a React di rieseguire il componente
// Destrutturazione dell'array restituito
const [selectedTopic, setSelectedTopic] = useState('components');
// selectedTopic contiene il valore corrente ('components')
// setSelectedTopic è la funzione per aggiornare il valore
Aggiornamento asincrono dello stato

L’aggiornamento dello stato è asincrono. Quando si chiama setSelectedTopic, il nuovo valore non è immediatamente disponibile. React programma l’aggiornamento e riesegue il componente. Solo nel prossimo render il nuovo valore sarà disponibile. Quando il nuovo stato dipende dal valore precedente, si usa una funzione: setCount(prevCount => prevCount + 1).

Rendering Condizionale

Il rendering condizionale permette di mostrare contenuti diversi in base a determinate condizioni. Ci sono diversi modi per implementare questo in React.

Operatore Ternario

L’operatore ternario è utile quando si vuole scegliere tra due alternative:

function App() {
const [selectedTopic, setSelectedTopic] = useState(null);
return (
<div>
{selectedTopic ? (
<div>
<h3>{examples[selectedTopic].title}</h3>
<p>{examples[selectedTopic].description}</p>
</div>
) : (
<p>Seleziona un argomento</p>
)}
</div>
);
}

Operatore AND (&&)

L’operatore && è utile quando si vuole mostrare qualcosa solo se una condizione è vera:

function App() {
const [selectedTopic, setSelectedTopic] = useState(null);
return (
<div>
{/* Mostra solo se selectedTopic è truthy */}
{selectedTopic && (
<div>
<h3>{examples[selectedTopic].title}</h3>
</div>
)}
{/* Mostra solo se selectedTopic è falsy */}
{!selectedTopic && (
<p>Seleziona un argomento</p>
)}
</div>
);
}
Variabile per contenuto condizionale

Un altro approccio è memorizzare il contenuto JSX in una variabile prima del return:

function App() {
const [selectedTopic, setSelectedTopic] = useState(null);
// Determinare il contenuto da mostrare
let content;
if (selectedTopic) {
content = (
<div>
<h3>{examples[selectedTopic].title}</h3>
<p>{examples[selectedTopic].description}</p>
</div>
);
} else {
content = <p>Seleziona un argomento</p>;
}
return (
<div>
{content}
</div>
);
}

Questo approccio mantiene il JSX più pulito quando la logica condizionale è complessa.

Styling Dinamico

Gli stili CSS possono essere applicati dinamicamente in base allo stato. In JSX, l’attributo class si chiama className (perché class è una parola riservata in JavaScript).

Applicare Classi Dinamicamente

function TabButton({ children, isSelected, onSelect }) {
return (
<li>
<button
className={isSelected ? 'active' : ''}
onClick={onSelect}
>
{children}
</button>
</li>
);
}
// Utilizzo
function App() {
const [selectedTopic, setSelectedTopic] = useState('components');
return (
<TabButton
isSelected={selectedTopic === 'components'}
onSelect={() => setSelectedTopic('components')}
>
Components
</TabButton>
);
}
Perché className invece di class?

In JSX si usa className invece di class perché class è una parola riservata in JavaScript. className è la proprietà DOM equivalente all’attributo HTML class.

Rendering di Liste Dinamiche

Spesso si ha bisogno di renderizzare una lista di elementi basata su un array di dati. Il metodo map() è lo strumento perfetto per questo scopo.

Usare map() per Liste Dinamiche

function App() {
const coreConcepts = [
{ title: 'Components', description: '...', image: componentsImg },
{ title: 'JSX', description: '...', image: jsxImg },
{ title: 'Props', description: '...', image: propsImg },
{ title: 'State', description: '...', image: stateImg }
];
return (
<ul>
{coreConcepts.map((concept) => (
<CoreConcept
key={concept.title}
title={concept.title}
description={concept.description}
image={concept.image}
/>
))}
</ul>
);
}
La prop key

La prop key è speciale e obbligatoria quando si renderizzano liste. Deve essere un valore univoco per ogni elemento della lista:

{coreConcepts.map((concept) => (
<CoreConcept
key={concept.title} // Deve essere unico
title={concept.title}
/>
))}

React usa key per identificare quali elementi sono cambiati, aggiunti o rimossi. La key deve essere un valore univoco e stabile (non cambiare tra i render). Si usano ID univoci quando disponibili, evitando l’indice dell’array se la lista può essere riordinata.

Spread Operator con Liste

Quando i dati sono già organizzati come oggetti con proprietà che corrispondono alle props, si può combinare map() con lo spread operator:

function App() {
const coreConcepts = [
{ title: 'Components', description: '...', image: componentsImg },
{ title: 'JSX', description: '...', image: jsxImg }
];
return (
<ul>
{coreConcepts.map((concept) => (
<CoreConcept
key={concept.title}
{...concept} // Spread di tutte le proprietà
/>
))}
</ul>
);
}

Componenti: funzioni JavaScript che restituiscono JSX, con nomi che iniziano con maiuscola. Si usano come elementi HTML: <Header />.

JSX: sintassi estesa di JavaScript per descrivere interfacce utente. Le parentesi graffe {} permettono di inserire valori dinamici. In JSX si usa className invece di class.

Props: meccanismo per passare dati dai componenti padre ai figli. Si destrutturano nei parametri: function Component({ prop1, prop2 }). Lo spread operator permette di passare tutte le proprietà: <Component {...data} />. La prop children rappresenta il contenuto tra i tag di apertura e chiusura.

State: dati gestiti da React che, quando cambiano, causano il re-render del componente. useState restituisce un array con valore corrente e funzione di aggiornamento: const [value, setValue] = useState(initialValue). L’aggiornamento è asincrono. Quando il nuovo stato dipende dal valore precedente, si usa la forma funzionale: setValue(prev => newValue).

Eventi: gestiti con props che iniziano con on: onClick={handleClick}. Si passa il riferimento alla funzione, non si esegue (onClick={handleClick}, non onClick={handleClick()}).

Rendering condizionale: operatore ternario {condition ? <A /> : <B />} o operatore AND {condition && <A />}.

Liste dinamiche: map() per renderizzare array: {array.map(item => <Item key={item.id} {...item} />)}. La prop key è obbligatoria e deve essere univoca e stabile.

Continua la lettura

Leggi il prossimo capitolo: "Approfondimento sui Concetti Essenziali di React"

Continua a leggere