Props
Cosa Sono le Props
Le props (abbreviazione di “properties”) sono il meccanismo principale per passare dati da un componente genitore a un componente figlio. Funzionano come gli attributi HTML, ma possono trasportare qualsiasi valore JavaScript: stringhe, numeri, oggetti, array, funzioni e persino altri componenti.
// Il genitore passa le props
function App() {
return <Saluto nome="Marco" eta={28} />;
}
// Il figlio riceve le props come oggetto
function Saluto(props) {
return (
<p>Ciao, {props.nome}! Hai {props.eta} anni.</p>
);
}
Le Props Sono di Sola Lettura
Una regola fondamentale di React: le props sono immutabili. Un componente non deve mai modificare le proprie props.
// SCORRETTO: mai mutare le props
function Componente(props) {
props.nome = "Altro"; // ERRORE concettuale!
return <p>{props.nome}</p>;
}
// CORRETTO: usa le props in sola lettura
function Componente(props) {
return <p>{props.nome}</p>;
}
Questo principio garantisce un flusso di dati unidirezionale (top-down), rendendo l’applicazione più prevedibile e facile da debuggare.
Destructuring delle Props
Il destructuring è il modo più comune e leggibile per accedere alle props.
// Destructuring nei parametri (consigliato)
function ProfiloUtente({ nome, email, ruolo }) {
return (
<div className="profilo">
<h2>{nome}</h2>
<p>{email}</p>
<span className="badge">{ruolo}</span>
</div>
);
}
// Destructuring nel corpo della funzione
function ProfiloUtente(props) {
const { nome, email, ruolo } = props;
return (
<div className="profilo">
<h2>{nome}</h2>
<p>{email}</p>
<span className="badge">{ruolo}</span>
</div>
);
}
Rest Props con Spread
Il pattern rest/spread è utile per passare props rimanenti a un elemento sottostante.
function Pulsante({ variante, children, ...rest }) {
return (
<button className={`btn btn-${variante}`} {...rest}>
{children}
</button>
);
}
// Tutte le props extra (onClick, disabled, type, ecc.)
// vengono passate direttamente al <button>
<Pulsante variante="primario" onClick={handleClick} disabled={false} type="submit">
Invia
</Pulsante>
Tipi di Props
Le props possono trasportare qualsiasi tipo di dato JavaScript.
function EsempioProps({
// Stringhe
nome, // "Marco"
// Numeri
eta, // 28
// Booleani
isAttivo, // true
// Array
hobby, // ["calcio", "lettura"]
// Oggetti
indirizzo, // { citta: "Roma", cap: "00100" }
// Funzioni (callback)
onClick, // () => console.log("click")
// Componenti React (JSX)
icona, // <IconaUtente />
// Elementi React
header, // <h1>Titolo</h1>
}) {
return <div>{/* ... */}</div>;
}
Passare Props Booleane
Per le props booleane, la sola presenza del nome equivale a true.
// Queste due righe sono equivalenti
<Input disabilitato={true} />
<Input disabilitato />
// Per passare false, devi essere esplicito
<Input disabilitato={false} />
Passare Oggetti e Array
function App() {
const utente = {
nome: "Marco",
email: "marco@example.com",
hobby: ["calcio", "lettura", "coding"],
};
return <ProfiloUtente utente={utente} />;
// Oppure spread dell'oggetto come props individuali
// return <ProfiloUtente {...utente} />;
}
function ProfiloUtente({ utente }) {
return (
<div>
<h2>{utente.nome}</h2>
<p>{utente.email}</p>
<ul>
{utente.hobby.map((h) => (
<li key={h}>{h}</li>
))}
</ul>
</div>
);
}
Props con Valori Predefiniti
Puoi assegnare valori di default alle props in diversi modi.
Destructuring con Valori Default
function Pulsante({
testo = "Click",
variante = "primario",
dimensione = "medio",
disabilitato = false,
}) {
return (
<button
className={`btn btn-${variante} btn-${dimensione}`}
disabled={disabilitato}
>
{testo}
</button>
);
}
// Uso senza specificare le props opzionali
<Pulsante /> // Usa tutti i valori default
<Pulsante testo="Invia" variante="secondario" />
defaultProps (Legacy)
defaultProps è un pattern precedente, ancora funzionante ma non più consigliato per i componenti funzionali.
// NON consigliato per componenti funzionali
function Pulsante({ testo, variante }) {
return <button className={`btn-${variante}`}>{testo}</button>;
}
Pulsante.defaultProps = {
testo: "Click",
variante: "primario",
};
Validazione con PropTypes
PropTypes è una libreria che aggiunge controllo dei tipi a runtime per le props. Utile in progetti JavaScript puro (senza TypeScript).
npm install prop-types
import PropTypes from "prop-types";
function Card({ titolo, descrizione, valutazione, tags, onClose }) {
return (
<div className="card">
<button onClick={onClose}>Chiudi</button>
<h2>{titolo}</h2>
<p>{descrizione}</p>
<span>Valutazione: {valutazione}/5</span>
<div>{tags.join(", ")}</div>
</div>
);
}
Card.propTypes = {
titolo: PropTypes.string.isRequired,
descrizione: PropTypes.string.isRequired,
valutazione: PropTypes.number,
tags: PropTypes.arrayOf(PropTypes.string),
onClose: PropTypes.func,
};
Card.defaultProps = {
valutazione: 0,
tags: [],
};
Tipi PropTypes Disponibili
PropTypes.string // Stringa
PropTypes.number // Numero
PropTypes.bool // Booleano
PropTypes.func // Funzione
PropTypes.array // Array
PropTypes.object // Oggetto
PropTypes.node // Qualsiasi renderizzabile
PropTypes.element // Elemento React
PropTypes.instanceOf(Date) // Istanza di una classe
PropTypes.oneOf(["a", "b"]) // Valore tra opzioni specifiche
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
PropTypes.arrayOf(PropTypes.number) // Array di numeri
PropTypes.shape({ // Oggetto con forma specifica
nome: PropTypes.string,
eta: PropTypes.number,
})
Tipizzazione Props con TypeScript
TypeScript è la soluzione moderna e raccomandata per la tipizzazione delle props. I tipi vengono verificati a compile-time, non a runtime.
Interface per le Props
interface CardProps {
titolo: string;
descrizione: string;
immagine?: string; // Opzionale
valutazione?: number; // Opzionale
variante: "default" | "evidenziata"; // Union type
tags: string[]; // Array di stringhe
autore: { // Oggetto tipizzato
nome: string;
avatar: string;
};
onClose: () => void; // Callback senza parametri
onValuta: (voto: number) => void; // Callback con parametro
}
function Card({
titolo,
descrizione,
immagine,
valutazione = 0,
variante,
tags,
autore,
onClose,
onValuta,
}: CardProps) {
return (
<div className={`card card-${variante}`}>
<button onClick={onClose}>X</button>
{immagine && <img src={immagine} alt={titolo} />}
<h2>{titolo}</h2>
<p>{descrizione}</p>
<div>
<img src={autore.avatar} alt={autore.nome} />
<span>{autore.nome}</span>
</div>
</div>
);
}
Type vs Interface
// Con type (equivalente per la maggior parte dei casi)
type CardProps = {
titolo: string;
descrizione: string;
};
// Con interface (supporta estensione e merging)
interface CardProps {
titolo: string;
descrizione: string;
}
// Estensione di interfacce
interface CardConImmagineProps extends CardProps {
immagine: string;
altText: string;
}
Pattern di Comunicazione Genitore-Figlio
Callback Props
Le funzioni passate come props permettono ai figli di comunicare con il genitore.
function Genitore() {
const [messaggi, setMessaggi] = useState<string[]>([]);
const aggiungiMessaggio = (testo: string) => {
setMessaggi((prev) => [...prev, testo]);
};
return (
<div>
<FormMessaggio onInvia={aggiungiMessaggio} />
<ListaMessaggi messaggi={messaggi} />
</div>
);
}
function FormMessaggio({ onInvia }: { onInvia: (testo: string) => void }) {
const [testo, setTesto] = useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
onInvia(testo);
setTesto("");
};
return (
<form onSubmit={handleSubmit}>
<input value={testo} onChange={(e) => setTesto(e.target.value)} />
<button type="submit">Invia</button>
</form>
);
}
function ListaMessaggi({ messaggi }: { messaggi: string[] }) {
return (
<ul>
{messaggi.map((msg, i) => (
<li key={i}>{msg}</li>
))}
</ul>
);
}
Best Practices
- Usa il destructuring nei parametri per chiarezza
- Fornisci valori default per le props opzionali
- Preferisci TypeScript a PropTypes per la tipizzazione
- Evita lo spread indiscriminato (
{...props}) che rende difficile capire quali props vengono passate - Nomina le callback props con il prefisso
on(es.onClick,onSubmit,onValutazioneChange) - Mantieni le props al minimo necessario: se un componente ha troppe props, probabilmente va suddiviso
Riepilogo
Le props sono il canale di comunicazione tra componenti React. Scorrono in un’unica direzione (da genitore a figlio), sono immutabili e possono trasportare qualsiasi tipo di dato. Usa il destructuring per leggibilita, TypeScript per la sicurezza dei tipi, e callback props per la comunicazione dal figlio al genitore.