Menu

La prop children in React: componenti wrapper e slot

children è la prop che contiene tutto ciò che metti tra il tag di apertura e quello di chiusura di un componente. Impara a costruire con essa componenti wrapper e di layout, a passare più slot come props con nome, a usare children come funzione, e quando la Children API è lo strumento sbagliato.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

In React, children è la prop che contiene tutto ciò che scrivi tra il tag di apertura e quello di chiusura di un componente. Un componente che renderizza {children} da qualche parte nel suo output diventa un wrapper: fornisce la cornice, e chi lo usa fornisce il contenuto.

Le due card condividono un bordo, un padding e uno stile del titolo, ma ognuna contiene un contenuto completamente diverso. Aggiungi una terza <Card title="Help"> con un link dentro e riceve la stessa cornice senza alcuno sforzo.

Come children arriva al componente

JSX trasforma il contenuto annidato in una prop. Queste due righe producono lo stesso elemento:

<Card title="Profile"><p>Ada</p></Card>

<Card title="Profile" children={<p>Ada</p>} />

Quindi children è una prop normale con un modo speciale di essere passata. La destrutturi come qualsiasi altra (function Card({ children })) o la leggi come props.children. La pagina sulle props tratta le props in generale.

Ciò che arriva in children dipende da cosa hai scritto tra i tag. Un solo figlio arriva come quel figlio. Più figli arrivano come array. Il testo arriva come stringa, e se non c'è nulla arriva undefined.

La console mostra object, array of 2, string e undefined. Raramente devi preoccupartene: renderizzare {children} gestisce tutti questi casi, e null, undefined, true e false non renderizzano nulla. La forma conta solo se provi a ispezionare o modificare i children, cosa da cui mette in guardia l'ultima sezione.

Componenti di layout

L'uso più comune di children è un componente di layout: la struttura di una pagina, un layout con barra laterale, una colonna centrata. Il componente possiede la struttura e la spaziatura, e ogni pagina che lo usa passa il proprio contenuto.

function PageLayout({ children }) {
    return (
        <div className="page">
            <Header />
            <main className="page-content">{children}</main>
            <Footer />
        </div>
    );
}

function AboutPage() {
    return (
        <PageLayout>
            <h1>About us</h1>
            <p>We teach people to code.</p>
        </PageLayout>
    );
}

A PageLayout non interessa cosa contenga una pagina "chi siamo". Questa separazione è proprio lo scopo: cambi l'intestazione una volta e ogni pagina racchiusa nel layout la riceve.

Più slot con props con nome

children è un solo slot. Quando un componente ha bisogno di contenuto in più di un punto, passa i pezzi in più come props con nome. Qualsiasi prop può contenere JSX, quindi una modale può ricevere un title, un footer e il suo corpo come children.

Il guscio della modale controlla bordi, padding e ordine delle tre aree, e chi la usa riempie ciascuna. Altri framework li chiamano slot; in React sono semplicemente props. Il <>...</> attorno ai due pulsanti è un fragment, che li raggruppa senza aggiungere un elemento in più. Una vera finestra di dialogo verrebbe anche renderizzata in document.body con un portal, così da stare sopra la pagina.

Sposta il <p> nella prop title e il testo del corpo compare nell'intestazione: il componente decide dove va ogni prop, chi lo usa decide solo cosa ci mette dentro.

Children come funzione

A volte il wrapper possiede un certo stato e chi lo usa dovrebbe decidere come disegnarlo. Invece di un elemento, chi lo usa passa una funzione come children, e il wrapper la chiama con i valori. Questo schema si chiama render prop.

Un Toggle disegna un pulsante e l'altro una checkbox, a partire dalla stessa logica. Le render prop erano il modo principale per condividere logica con stato prima degli hook. Oggi un hook personalizzato come useToggle() di solito si legge meglio, ma vedrai ancora le render prop in librerie per liste, form e animazioni.

La Children API e cloneElement

React esporta un oggetto Children con funzioni di supporto per scorrere children: Children.map, Children.forEach, Children.count, Children.toArray e Children.only. Insieme a cloneElement, che copia un elemento con nuove props, permettono a un genitore di ispezionare e modificare ciò che ha ricevuto. Il team di React le elenca come API legacy: funzionano ancora, ma il codice nuovo dovrebbe evitarle.

Il motivo è che vedono solo gli elementi scritti direttamente tra i tag. Non possono vedere ciò che quegli elementi renderizzano.

La lista mostra quattro elementi, ma la console dice Children.count = 3, perché TwoMore è un solo figlio indipendentemente da quanti elementi renderizzi. cloneElement passa anche la prop style a TwoMore, che la ignora, quindi il terzo e il quarto elemento non sono a righe alterne. Chiunque trasformi alcuni elementi <li> in un componente rompe il genitore senza toccarlo.

La soluzione è smettere di frugare nei children e passare invece dei dati:

function NumberedList({ items }) {
    return (
        <ol>
            {items.map((item, i) => (
                <li key={item.id} style={{ color: i % 2 ? 'gray' : 'black' }}>
                    {item.label}
                </li>
            ))}
        </ol>
    );
}

Ora la lista si occupa del rendering di ogni riga, e nulla dipende da come chi la usa ha scritto il suo JSX. Quando un genitore deve condividere valori con figli annidati in profondità (una scheda selezionata, un tema), usa il context invece di clonare props su di essi.

Tipizzare children in TypeScript

In TypeScript, tipizza children come React.ReactNode. Copre tutto ciò che React può renderizzare: elementi, stringhe, numeri, array di questi, null, undefined e booleani.

import type { ReactNode } from 'react';

type CardProps = {
    title: string;
    children: ReactNode;
};

function Card({ title, children }: CardProps) {
    return (
        <section>
            <h3>{title}</h3>
            {children}
        </section>
    );
}

Per una render prop, tipizzala come la funzione che è: children: (on: boolean, toggle: () => void) => ReactNode. Rendi children facoltativa (children?: ReactNode) quando il componente ha senso anche vuoto.

Errori comuni

Dimenticare di renderizzare children. Se un componente accetta children ma non mette mai {children} nel suo output, il contenuto sparisce in silenzio. Nulla ti avvisa.

Chiamare children come una funzione quando non lo è. children() funziona solo quando chi usa il componente ha passato una funzione. Se un componente si aspetta una render prop, dillo nel suo nome o nella documentazione, oppure accetta una prop con nome come render così che l'intenzione sia chiara.

Modificare children. Gli elementi sono di sola lettura. In sviluppo React li congela, quindi assegnare a children.props lì genera un errore. Costruisci invece un nuovo output.

Domande frequenti

Cos'è props.children in React?

È il contenuto scritto tra il tag di apertura e quello di chiusura di un componente. In <Card><p>Hi</p></Card>, la funzione Card riceve il <p> come props.children e decide dove renderizzarlo.

children è una prop speciale?

Solo nel modo in cui la passi. JSX mette il contenuto annidato in una prop chiamata children, ma dentro il componente è una prop normale. Puoi anche passarla in modo esplicito, come in <Card children={<p>Hi</p>} />, e funziona allo stesso modo.

Come passo più di un blocco di contenuto a un componente?

Usa props con nome per i blocchi in più. Una prop può contenere JSX proprio come children, quindi <Modal title={<h2>Delete?</h2>} footer={<button>OK</button>}>Body</Modal> dà al componente tre slot.

Dovrei usare Children.map e cloneElement?

Evitali nel codice nuovo. Vedono solo gli elementi scritti direttamente tra i tag, non ciò che quegli elementi renderizzano, quindi si rompono non appena qualcuno racchiude un figlio in un altro componente. Passa invece un array di dati come prop, oppure usa il context.

Che tipo ha children in TypeScript?

Usa React.ReactNode. Copre tutto ciò che React può renderizzare: elementi, stringhe, numeri, array, null, undefined e booleani.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA