Children
Children
memungkinkan Anda memanipulasi dan mengubah JSX yang Anda terima sebagai prop children
.
const mappedChildren = Children.map(children, child =>
<div className="Row">
{child}
</div>
);
- Referensi
- Penggunaan
- Alternatif
- Hal ini tidak akan bekerja dengan
Children.map
karena fungsi tersebut akan “melihat”<MoreRows />
sebagai satu anak (dan satu baris). - Pemecahan Masalah
Referensi
Children.count(children)
Panggil Children.count(children)
untuk menghitung jumlah anak dalam struktur data children
.
import { Children } from 'react';
function RowList({ children }) {
return (
<>
<h1>Jumlah baris: {Children.count(children)}</h1>
...
</>
);
}
Lihat contoh lainnya di bawah ini.
Parameter
children
: Nilai dari propchildren
yang diterima oleh komponen Anda.
Kembalian
Jumlah simpul dalam children
ini.
Catatan Penting
- Simpul kosong (
null
,undefined
, dan Boolean), string, angka, dan elemen React dihitung sebagai simpul individu. Larik tidak dihitung sebagai simpul individu, tetapi anak-anaknya dihitung sebagai simpul individu. Penjelajahan tidak masuk lebih dalam dari elemen React: mereka tidak di-render, dan anak-anaknya tidak dijelajahi. Fragmen tidak dijelajahi.
Children.forEach(children, fn, thisArg?)
Panggil Children.forEach(children, fn, thisArg?)
untuk menjalankan beberapa kode untuk setiap anak dalam struktur data children
.
import { Children } from 'react';
function SeparatorList({ children }) {
const result = [];
Children.forEach(children, (child, index) => {
result.push(child);
result.push(<hr key={index} />);
});
// ...
Lihat contoh lainnya di bawah ini.
Parameter
children
: Nilai dari propchildren
yang diterima oleh komponen Anda.fn
: Fungsi yang ingin Anda jalankan untuk setiap anak, serupa dengan metode callback dari larikforEach
. Fungsi ini akan dipanggil dengan anak sebagai argumen pertama dan indeksnya sebagai argumen kedua. Indeks dimulai dari0
dan bertambah pada setiap pemanggilan.- opsional
thisArg
: Nilaithis
yang digunakan untuk memanggil fungsifn
. Jika diabaikan, maka akan menjadiundefined
.
Kembalian
Children.forEach
mengembalikan undefined
.
Catatan Penting
- Simpul kosong (
null
,undefined
, dan Boolean), string, angka, dan elemen React dihitung sebagai simpul individual. Larik tidak dihitung sebagai simpul individu, tetapi anak-anaknya dihitung sebagai simpul individu. Penjelajahan tidak masuk lebih dalam dari elemen React: mereka tidak di-render, dan anak-anaknya tidak dijelajahi. Fragmen tidak dijelajahi.
Children.map(children, fn, thisArg?)
Panggil Children.map(children, fn, thisArg?)
untuk memetakan atau mentransformasi setiap anak dalam struktur data children
.
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}
Lihat contoh lainnya di bawah ini.
Parameter
children
: Nilai dari propchildren
yang diterima oleh komponen Anda.fn
: Fungsi pemetaan, mirip dengan metode callback dari larikmap
. Fungsi ini akan dipanggil dengan anak sebagai argumen pertama dan indeksnya sebagai argumen kedua. Indeks dimulai dari0
dan bertambah pada setiap pemanggilan. Anda harus mengembalikan sebuah simpul React dari fungsi ini. Node ini dapat berupa node kosong (null
,undefined
, atau Boolean), string, angka, elemen React, atau larik simpul React lainnya.- opsional
thisArg
: Nilaithis
yang digunakan untuk memanggil fungsifn
. Jika diabaikan, maka akan menjadiundefined
.
Kembalian
Jika children
adalah null
atau undefined
, akan mengembalikan nilai yang sama.
Jika tidak, kembalikan larik flat yang terdiri dari simpul yang Anda kembalikan dari fungsi fn
. Larik yang dikembalikan akan berisi semua simpul yang Anda kembalikan kecuali null
dan undefined
.
Catatan Penting
-
Simpul kosong (
null
,undefined
, dan Boolean), string, angka, dan elemen React dihitung sebagai simpul individual. Larik tidak dihitung sebagai simpul individu, tetapi anak-anaknya dihitung sebagai simpul individu. Penjelajahan tidak masuk lebih dalam dari elemen React: mereka tidak di-render, dan anak-anaknya tidak dijelajahi. Fragmen tidak dijelajahi. -
Jika Anda mengembalikan sebuah elemen atau larik elemen dengan kunci dari
fn
, kunci elemen yang dikembalikan akan secara otomatis digabungkan dengan kunci item asli yang sesuai darichildren
. Jika Anda mengembalikan beberapa elemen darifn
dalam sebuah larik, kuncinya hanya perlu unik secara lokal satu sama lain.
Children.only(children)
Panggil Children.only(children)
untuk menyatakan bahwa children
merepresentasikan satu elemen React.
function Box({ children }) {
const element = Children.only(children);
// ...
Parameter
children
: Nilai dari propchildren
yang diterima oleh komponen Anda.
Kembalian
Jika children
adalah elemen yang valid, kembalikan elemen tersebut.
Jika tidak, akan lemparkan sebuah error.
Catatan Penting
- Metode ini selalu melempar jika Anda mengoper sebuah array (seperti nilai kembalian dari
Children.map
) sebagaichildren
. Dengan kata lain, metode ini memaksakan bahwachildren
adalah sebuah elemen React, bukan sebuah array dengan satu elemen.
Children.toArray(children)
Panggil Children.toArray(children)
untuk membuat larik dari struktur data children
.
import { Children } from 'react';
export default function ReversedList({ children }) {
const result = Children.toArray(children);
result.reverse();
// ...
Parameter
children
: Nilai dari propchildren
yang diterima oleh komponen Anda.
Kembalian
Mengembalikan larik yang flat dari elemen dalam children
.
Catatan Penting
- Simpul kosong (
null
,undefined
, dan Boolean) akan dihilangkan dalam larik yang dikembalikan. Kunci elemen yang dikembalikan akan dihitung dari kunci elemen asli dan tingkat persarangan serta posisinya. Hal ini memastikan bahwa pe-flatten-an larik tidak mengakibatkan perubahan perilaku.
Penggunaan
Mentransformasikan anak-anak
Untuk mentransformasi anak-anak JSX yang diterima komponen Anda sebagai prop children
, panggil Children.map
:
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}
Pada contoh di atas, RowList
membungkus setiap anak yang diterimanya ke dalam wadah <div className="Row">
. Sebagai contoh, anggaplah komponen induk meneruskan tiga tag <p>
sebagai props children
ke RowList
:
<RowList>
<p>Ini adalah butir pertama.</p>
<p>Ini adalah butir kedua</p>
<p>Ini adalah butir ketiga.</p>
</RowList>
Kemudian, dengan implementasi RowList
di atas, hasil akhir yang di-render akan terlihat seperti ini:
<div className="RowList">
<div className="Row">
<p>Ini adalah butir pertama.</p>
</div>
<div className="Row">
<p>Ini adalah butir kedua</p>
</div>
<div className="Row">
<p>Ini adalah butir ketiga.</p>
</div>
</div>
Children.map
mirip dengan mentransformasi array dengan map()
. Perbedaannya adalah bahwa struktur data children
dianggap sebagai buram. Artinya, meskipun terkadang children
berupa array, Anda tidak boleh mengasumsikannya sebagai array atau tipe data tertentu lainnya. Inilah sebabnya mengapa Anda harus menggunakan Children.map
jika Anda perlu melakukan transformasi.
import { Children } from 'react'; export default function RowList({ children }) { return ( <div className="RowList"> {Children.map(children, child => <div className="Row"> {child} </div> )} </div> ); }
Pendalaman
Dalam React, prop children
dianggap sebagai struktur data buram. Artinya, Anda tidak boleh bergantung pada cara penytrukturannya. Untuk mengubah, memfilter, atau menghitung anak, Anda harus menggunakan metode-metode Children
.
Pada praktiknya, struktur data children
sering kali direpresentasikan sebagai sebuah larik secara internal. Namun, jika hanya ada satu child, maka React tidak akan membuat larik tambahan karena hal ini akan menyebabkan overhead memori yang tidak diperlukan. Selama Anda menggunakan metode Children
dan tidak secara langsung mengintrospeksi prop children
, kode Anda tidak akan rusak meskipun React mengganti bagaimana struktur datanya diimplementasikan.
Bahkan saat children
berupa sebuah larik, Children.map
memiliki perilaku khusus yang membantu. Sebagai contoh, Children.map
menggabungkan beberapa key pada elemen yang dikembalikan dengan kunci pada children
yang telah Anda berikan padanya. Hal ini memastikan anak JSX asli tidak “kehilangan” kunci meskipun dibungkus seperti pada contoh di atas.
Menjalankan beberapa kode untuk setiap anak
Panggil Children.forEach
untuk mengulang setiap anak dalam struktur data children
. Metode ini tidak mengembalikan nilai apa pun dan mirip dengan metode larik forEach
. Anda dapat menggunakannya untuk menjalankan logika khusus seperti membuat larik Anda sendiri.
import { Children } from 'react'; export default function SeparatorList({ children }) { const result = []; Children.forEach(children, (child, index) => { result.push(child); result.push(<hr key={index} />); }); result.pop(); // Hapus *separator* terakhir return result; }
import { Children } from 'react'; export default function RowList({ children }) { return ( <div className="RowList"> <h1 className="RowListHeader"> Jumlah baris: {Children.count(children)} </h1> {Children.map(children, child => <div className="Row"> {child} </div> )} </div> ); }
Mengonversi anak menjadi larik
Panggil Children.toArray(children)
untuk mengubah struktur data children
menjadi larik JavaScript biasa. Hal ini memungkinkan Anda memanipulasi larik dengan metode larik bawaan seperti filter
, sort
, atau reverse
.
import { Children } from 'react'; export default function ReversedList({ children }) { const result = Children.toArray(children); result.reverse(); return result; }
Alternatif
Mengekspos beberapa komponen
Memanipulasi anak dengan metode Children
sering kali menghasilkan kode yang mudah rusak. Ketika Anda mengoper children ke sebuah komponen di JSX, biasanya Anda tidak mengharapkan komponen tersebut untuk memanipulasi atau mengubah masing-masing children.
Jika memungkinkan, hindari penggunaan metode Children
. Misalnya, jika Anda ingin setiap anak dari RowList
dibungkus dengan <div className="Row">
, ekspor komponen Row
, dan secara manual membungkus setiap baris ke dalamnya seperti ini:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList> <Row> <p>Ini adalah butir pertama.</p> </Row> <Row> <p>Ini adalah butir kedua</p> </Row> <Row> <p>Ini adalah butir ketiga.</p> </Row> </RowList> ); }
Tidak seperti menggunakan Children.map
, pendekatan ini tidak membungkus setiap anak secara otomatis. **Namun, pendekatan ini memiliki manfaat yang signifikan dibandingkan dengan contoh sebelumnya dengan Children.map
karena pendekatan ini tetap bekerja meskipun Anda terus mengekstrak lebih banyak komponen, sebagai contoh, pendekatan ini tetap bekerja jika Anda mengekstrak komponen MoreRows
Anda sendiri:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList> <Row> <p>Ini adalah butir pertama.</p> </Row> <MoreRows /> </RowList> ); } function MoreRows() { return ( <> <Row> <p>Ini adalah butir kedua</p> </Row> <Row> <p>Ini adalah butir ketiga.</p> </Row> </> ); }
Hal ini tidak akan bekerja dengan Children.map
karena fungsi tersebut akan “melihat” <MoreRows />
sebagai satu anak (dan satu baris).
Menerima larik objek sebagai prop
Anda juga bisa secara eksplisit mengoper larik sebagai prop. Sebagai contoh, RowList
ini menerima larik baris
sebagai prop:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList rows={[ { id: 'first', content: <p>Ini adalah butir pertama.</p> }, { id: 'second', content: <p>Ini adalah butir kedua</p> }, { id: 'third', content: <p>Ini adalah butir ketiga.</p> } ]} /> ); }
Karena rows
adalah larik JavaScript biasa, komponen RowList
dapat menggunakan metode larik bawaan seperti map
di dalamnya.
Pola ini sangat berguna ketika Anda ingin memberikan lebih banyak informasi sebagai data terstruktur bersama dengan anak. Pada contoh di bawah ini, komponen TabSwitcher
menerima larik objek sebagai prop tabs
:
import TabSwitcher from './TabSwitcher.js'; export default function App() { return ( <TabSwitcher tabs={[ { id: 'first', header: 'First', content: <p>Ini adalah butir pertama.</p> }, { id: 'second', header: 'Second', content: <p>Ini adalah butir kedua</p> }, { id: 'third', header: 'Third', content: <p>Ini adalah butir ketiga.</p> } ]} /> ); }
Tidak seperti mengoper anak-anak sebagai JSX, pendekatan ini memungkinkan Anda untuk mengasosiasikan beberapa data tambahan seperti header
dengan setiap item. Karena Anda bekerja dengan tabs
secara langsung, dan tabs
adalah sebuah larik sehingga Anda tidak memerlukan metode Children
.
Memanggil render prop untuk menyesuaikan rendering
Daripada menghasilkan JSX untuk setiap item, Anda juga bisa mengoper fungsi yang mengembalikan JSX, dan memanggil fungsi tersebut bila diperlukan. Pada contoh ini, komponen Aplikasi
mengoper fungsi renderContent
ke komponen TabSwitcher
. Komponen TabSwitcher
memanggil renderContent
hanya untuk tab yang dipilih:
import TabSwitcher from './TabSwitcher.js'; export default function App() { return ( <TabSwitcher tabIds={['pertama', 'kedua', 'ketiga']} getHeader={tabId => { return tabId[0].toUpperCase() + tabId.slice(1); }} renderContent={tabId => { return <p>Ini adalah butir {tabId}.</p>; }} /> ); }
Sebuah prop seperti renderContent
disebut sebagai render prop karena merupakan prop yang menentukan bagaimana cara me-render sebuah bagian dari antarmuka pengguna. Namun, tidak ada yang aneh dengan prop ini: prop ini adalah prop biasa yang kebetulan merupakan sebuah fungsi.
Render props adalah fungsi, sehingga Anda dapat meneruskan informasi kepada mereka. Misalnya, komponen RowList
ini meneruskan id
dan index
dari setiap baris ke props render renderRow
, yang menggunakan index
untuk menonjolkan baris genap:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList rowIds={['pertama', 'kedua', 'ketiga']} renderRow={(id, index) => { return ( <Row isHighlighted={index % 2 === 0}> <p>Ini adalah butir {tabId}.</p> </Row> ); }} /> ); }
Demikianlah contoh lain bagaimana komponen induk dan anak dapat bekerja sama tanpa memanipulasi anak-anaknya.
Pemecahan Masalah
Saya mengoper komponen kustom, tetapi method Children
tidak menampilkan hasil render-nya
Misalkan Anda mengoper dua anak ke RowList
seperti ini:
<RowList>
<p>Butir pertama</p>
<MoreRows />
</RowList>
Jika Anda melakukan Children.count(children)
di dalam RowList
, Anda akan mendapatkan 2
. Bahkan jika MoreRows
me-render 10 item yang berbeda, atau jika mengembalikan null
, Children.count(children)
akan tetap menjadi 2
. Dari sudut pandang RowList
, ia hanya “melihat” JSX yang diterimanya. Ia tidak “melihat” bagian internal komponen MoreRows
.
Limitasi ini menyulitkan untuk mengekstrak sebuah komponen. Inilah sebabnya mengapa alternatif lebih disarankan daripada menggunakan Children
.