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
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. Senarai tidak dihitung sebagai simpul individu, tetapi anak-anaknya dihitung sebagai simpul individu. Traversal tidak masuk lebih dalam dari elemen React: mereka tidak di-render, dan anak-anaknya tidak di-traverse. Fragmen tidak di-traverse.
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 method callback dari senaraiforEach
. 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 nilainya akan menjadiundefined
.
Kembalian
Children.forEach
mengembalikan undefined
.
Catatan Penting
- Simpul kosong (
null
,undefined
, dan Boolean), string, angka, dan elemen React dihitung sebagai simpul individual. Senarai tidak dihitung sebagai simpul individu, tetapi anak-anaknya dihitung sebagai simpul individu. Traversal tidak masuk lebih dalam dari elemen React: mereka tidak di-render, dan anak-anaknya tidak di-traverse. Fragmen tidak di-traverse.
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 method callback dari senaraimap
. 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. Simpul ini dapat berupa simpul kosong (null
,undefined
, atau Boolean), string, angka, elemen React, atau senarai simpul React lainnya.- opsional
thisArg
: Nilaithis
yang digunakan untuk memanggil fungsifn
. Jika diabaikan, maka nilainya akan menjadiundefined
.
Kembalian
Jika children
adalah null
atau undefined
, maka method ini akan mengembalikan nilai yang sama.
Jika tidak, method ini akan mengembalikan senarai flat yang terdiri dari simpul yang Anda kembalikan dari fungsi fn
. Senarai 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. Senarai tidak dihitung sebagai simpul individu, tetapi anak-anaknya dihitung sebagai simpul individu. Traversal tidak masuk lebih dalam dari elemen React: mereka tidak di-render, dan anak-anaknya tidak di-traverse. Fragmen tidak di-traverse. -
Jika Anda mengembalikan sebuah elemen atau senarai 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 senarai, 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, elemen tersebut akan dikembalikan.
Jika tidak, maka lemparkan sebuah error.
Catatan Penting
- Method ini akan selalu melempar jika Anda mengoper sebuah senarai (seperti nilai kembalian dari
Children.map
) sebagaichildren
. Dengan kata lain, method ini memaksakan bahwachildren
adalah sebuah elemen React, bukan sebuah senarai dengan satu elemen.
Children.toArray(children)
Panggil Children.toArray(children)
untuk membuat senarai 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 senarai yang flat dari elemen dalam children
.
Catatan Penting
- Simpul kosong (
null
,undefined
, dan Boolean) akan dihilangkan dalam senarai yang dikembalikan. Kunci elemen yang dikembalikan akan dihitung dari kunci elemen asli dan tingkat persarangan serta posisinya. Hal ini memastikan bahwa pe-flatten-an senarai 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 senarai dengan map()
. Perbedaannya adalah bahwa struktur data children
dianggap sebagai buram. Artinya, meskipun terkadang children
berupa senarai, Anda tidak boleh mengasumsikannya sebagai senarai 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 method-method Children
.
Pada praktiknya, struktur data children
sering kali direpresentasikan sebagai sebuah senarai secara internal. Namun, jika hanya ada satu child, maka React tidak akan membuat senarai tambahan karena hal ini akan menyebabkan overhead memori yang tidak diperlukan. Selama Anda menggunakan method pada 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 senarai, 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 yang asli tidak “kehilangan” kunci meskipun dibungkus seperti pada contoh di atas.
Menjalankan beberapa kode untuk setiap anak
Panggil Children.forEach
untuk melakukan iterasi pada setiap anak dalam struktur data children
. Method ini tidak mengembalikan nilai apa pun dan mirip dengan method senarai forEach
. Anda dapat menggunakannya untuk menjalankan logika khusus seperti membuat senarai 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 senarai
Panggil Children.toArray(children)
untuk mengubah struktur data children
menjadi senarai JavaScript biasa. Hal ini memungkinkan Anda memanipulasi senarai dengan method senarai 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 method pada 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 method pada 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 keunggulan 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 senarai objek sebagai prop
Anda juga bisa secara eksplisit mengoper senarai sebagai prop. Sebagai contoh, RowList
ini menerima senarai 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 senarai JavaScript biasa, komponen RowList
dapat menggunakan method senarai 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 senarai 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 senarai sehingga Anda tidak memerlukan method 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 {id}.</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 butir 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
.