Di React, children adalah prop yang menampung apa pun yang kamu tulis di antara tag pembuka dan tag penutup sebuah komponen. Komponen yang merender {children} di suatu tempat dalam output-nya menjadi pembungkus: komponen itu menyediakan bingkainya, dan pemanggilnya menyediakan isinya.
Kedua kartu memakai border, padding, dan gaya judul yang sama, tetapi isinya sama sekali berbeda. Tambahkan <Card title="Help"> ketiga dengan sebuah link di dalamnya, dan kartu itu langsung mendapat bingkai yang sama.
Bagaimana children sampai ke komponen
JSX mengubah konten bersarang menjadi prop. Dua baris ini menghasilkan elemen yang sama:
<Card title="Profile"><p>Ada</p></Card>
<Card title="Profile" children={<p>Ada</p>} />
Jadi children adalah prop biasa dengan cara pengiriman yang khusus. Kamu bisa mengambilnya dengan destructuring seperti prop lain (function Card({ children })) atau membacanya sebagai props.children. Halaman tentang props membahas props secara umum.
Apa yang masuk ke children bergantung pada apa yang kamu tulis di antara tag. Satu child datang apa adanya. Beberapa child datang sebagai array. Teks datang sebagai string, dan jika tidak ada apa pun, yang datang adalah undefined.
Konsol menampilkan object, array of 2, string, dan undefined. Biasanya kamu tidak perlu memikirkannya: merender {children} sudah menangani semua bentuk itu, dan null, undefined, true, serta false tidak merender apa pun. Bentuknya baru penting kalau kamu mencoba memeriksa atau mengubah children, dan justru itu yang diperingatkan di bagian terakhir.
Komponen layout
Penggunaan children yang paling umum adalah komponen layout: kerangka halaman, layout dengan sidebar, kolom di tengah. Komponen itu mengatur struktur dan jaraknya, dan setiap halaman yang memakainya mengirim kontennya sendiri.
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>
);
}
PageLayout tidak tahu dan tidak peduli apa isi halaman about. Pemisahan inilah intinya: ubah header sekali, dan setiap halaman yang dibungkus layout itu ikut berubah.
Beberapa slot dengan props bernama
children hanyalah satu slot. Saat sebuah komponen butuh konten di lebih dari satu tempat, kirim bagian tambahannya sebagai props bernama. Prop apa pun bisa menampung JSX, jadi sebuah modal bisa menerima title, footer, dan isinya sebagai children.
Kerangka modal mengatur border, padding, dan urutan ketiga area itu, dan pemanggil mengisi masing-masing area. Framework lain menyebutnya slot; di React, semuanya hanyalah props. <>...</> yang membungkus kedua tombol adalah fragment, yang mengelompokkan keduanya tanpa menambah elemen ekstra. Dialog sungguhan juga akan dirender ke document.body dengan portal agar tampil di atas halaman.
Pindahkan <p> ke prop title, dan teks isinya muncul di header: komponen yang menentukan ke mana setiap prop ditempatkan, pemanggil hanya menentukan apa isinya.
Children sebagai fungsi
Kadang pembungkus memegang suatu state, dan pemanggil yang sebaiknya menentukan cara menggambarnya. Alih-alih elemen, pemanggil mengirim fungsi sebagai children, lalu pembungkus memanggilnya dengan nilai-nilai tersebut. Pola ini disebut render prop.
Satu Toggle menggambar tombol dan yang lain menggambar checkbox, dari logika yang sama. Render prop dulu adalah cara utama berbagi logika yang punya state sebelum ada hook. Sekarang, custom hook seperti useToggle() biasanya lebih mudah dibaca, tetapi kamu masih akan menemukan render prop di library untuk list, form, dan animasi.
API Children dan cloneElement
React mengekspor objek Children berisi helper untuk menelusuri children: Children.map, Children.forEach, Children.count, Children.toArray, dan Children.only. Bersama cloneElement, yang menyalin elemen dengan props baru, helper ini memungkinkan komponen induk memeriksa dan mengubah apa yang diberikan kepadanya. Tim React menggolongkannya sebagai API lawas: masih berfungsi, tetapi kode baru sebaiknya menghindarinya.
Alasannya, helper ini hanya melihat elemen yang ditulis langsung di antara tag. Helper ini tidak bisa melihat apa yang dirender elemen-elemen itu.
List menampilkan empat item, tetapi konsol menulis Children.count = 3, karena TwoMore dihitung sebagai satu child, berapa pun item yang direndernya. cloneElement juga memberikan prop style ke TwoMore, yang mengabaikannya, sehingga item ketiga dan keempat tidak bergaris warna. Siapa pun yang merapikan beberapa elemen <li> menjadi sebuah komponen akan merusak komponen induk tanpa menyentuhnya.
Solusinya adalah berhenti mengutak-atik children dan mengirim data sebagai gantinya:
function NumberedList({ items }) {
return (
<ol>
{items.map((item, i) => (
<li key={item.id} style={{ color: i % 2 ? 'gray' : 'black' }}>
{item.label}
</li>
))}
</ol>
);
}
Sekarang list mengatur cara merender setiap baris, dan tidak ada yang bergantung pada cara pemanggil menulis JSX-nya. Saat komponen induk perlu berbagi nilai dengan child yang bersarang dalam (tab yang dipilih, tema), pakai context, bukan menyalin props ke child-child itu.
Memberi tipe pada children di TypeScript
Di TypeScript, beri children tipe React.ReactNode. Tipe ini mencakup semua yang bisa dirender React: elemen, string, angka, array dari semua itu, null, undefined, dan boolean.
import type { ReactNode } from 'react';
type CardProps = {
title: string;
children: ReactNode;
};
function Card({ title, children }: CardProps) {
return (
<section>
<h3>{title}</h3>
{children}
</section>
);
}
Untuk render prop, beri tipe sesuai fungsinya: children: (on: boolean, toggle: () => void) => ReactNode. Jadikan children opsional (children?: ReactNode) jika komponen itu tetap masuk akal saat kosong.
Kesalahan umum
Lupa merender children. Jika sebuah komponen menerima children tetapi tidak pernah menaruh {children} di output-nya, kontennya hilang begitu saja. Tidak ada peringatan apa pun.
Memanggil children seperti fungsi padahal bukan fungsi. children() hanya berjalan jika pemanggil mengirim fungsi. Jika sebuah komponen mengharapkan render prop, sebutkan itu di nama atau dokumentasinya, atau terima prop bernama seperti render agar maksudnya jelas.
Mengubah children secara langsung. Elemen bersifat read only. React membekukannya saat development, jadi menulis ke children.props akan melempar error di sana. Buat output baru sebagai gantinya.
Pertanyaan yang sering diajukan
Apa itu props.children di React?
props.children adalah konten yang ditulis di antara tag pembuka dan tag penutup sebuah komponen. Pada <Card><p>Hi</p></Card>, fungsi Card menerima <p> sebagai props.children dan memutuskan di mana elemen itu dirender.
Apakah children prop yang istimewa?
Hanya dalam cara mengirimnya. JSX memasukkan konten bersarang ke prop bernama children, tetapi di dalam komponen ia hanyalah prop biasa. Kamu juga bisa mengirimnya secara eksplisit, seperti <Card children={<p>Hi</p>} />, dan hasilnya sama.
Bagaimana cara mengirim lebih dari satu blok konten ke sebuah komponen?
Pakai props bernama untuk blok tambahan. Sebuah prop bisa menampung JSX seperti children, jadi <Modal title={<h2>Delete?</h2>} footer={<button>OK</button>}>Body</Modal> memberi komponen itu tiga slot.
Apakah sebaiknya saya memakai Children.map dan cloneElement?
Hindari keduanya di kode baru. Keduanya hanya melihat elemen yang ditulis langsung di antara tag, bukan apa yang dirender elemen itu, jadi langsung rusak begitu seseorang membungkus sebuah child dengan komponen lain. Sebagai gantinya, kirim array data sebagai prop, atau pakai context.
Apa tipe children di TypeScript?
Pakai React.ReactNode. Tipe ini mencakup semua yang bisa dirender React: elemen, string, angka, array, null, undefined, dan boolean.