Sandungan

Menggunakan Children tidaklah umum dan dapat membuat kode Anda mudah rusak. Lihat alternatif umum.

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 prop children 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 prop children yang diterima oleh komponen Anda.
  • fn: Fungsi yang ingin Anda jalankan untuk setiap anak, serupa dengan method callback dari senarai forEach. Fungsi ini akan dipanggil dengan anak sebagai argumen pertama dan indeksnya sebagai argumen kedua. Indeks dimulai dari 0 dan bertambah pada setiap pemanggilan.
  • opsional thisArg: Nilai this yang digunakan untuk memanggil fungsi fn. Jika diabaikan, maka nilainya akan menjadi undefined.

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 prop children yang diterima oleh komponen Anda.
  • fn: Fungsi pemetaan, mirip dengan method callback dari senarai map. Fungsi ini akan dipanggil dengan anak sebagai argumen pertama dan indeksnya sebagai argumen kedua. Indeks dimulai dari 0 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: Nilai this yang digunakan untuk memanggil fungsi fn. Jika diabaikan, maka nilainya akan menjadi undefined.

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 dari children. Jika Anda mengembalikan beberapa elemen dari fn 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 prop children 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) sebagai children. Dengan kata lain, method ini memaksakan bahwa children 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 prop children 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

Mengapa prop children tidak selalu berupa senarai?

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.

Sandungan

Struktur data children tidak termasuk keluaran yang di-render dari komponen yang Anda berikan sebagai JSX. Pada contoh di bawah ini, children yang diterima oleh RowList hanya berisi dua butir, bukan tiga:

  1. <p>Ini adalah butir pertama.</p>
  2. <MoreRows />

Inilah alasan mengapa hanya dua pembungkus baris yang dihasilkan dalam contoh ini:

import RowList from './RowList.js';

export default function App() {
  return (
    <RowList>
      <p>Ini adalah butir pertama.</p>
      <MoreRows />
    </RowList>
  );
}

function MoreRows() {
  return (
    <>
      <p>Ini adalah butir kedua</p>
      <p>Ini adalah butir ketiga.</p>
    </>
  );
}

Tidak ada cara untuk mendapatkan keluaran yang di-render dari komponen dalam, seperti <MoreRows /> saat memanipulasi children. Inilah mengapa biasanya lebih baik menggunakan salah satu solusi alternatif.


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;
}

Sandungan

Seperti yang telah disebutkan sebelumnya, tidak ada cara untuk mendapatkan hasil render dari komponen dalam ketika memanipulasi children. Inilah sebabnya mengapa biasanya lebih baik menggunakan salah satu solusi alternatif.


Menghitung anak-anak

Panggil Children.count(children) untuk menghitung jumlah anak.

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>
  );
}

Sandungan

Seperti yang telah disebutkan sebelumnya, tidak ada cara untuk mendapatkan hasil render dari komponen dalam ketika memanipulasi children. Inilah sebabnya mengapa biasanya lebih baik menggunakan salah satu solusi alternatif.


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;
}

Sandungan

Seperti yang telah disebutkan sebelumnya, tidak ada cara untuk mendapatkan hasil render dari komponen dalam ketika memanipulasi children. Inilah sebabnya mengapa biasanya lebih baik menggunakan salah satu solusi alternatif.


Alternatif

Catatan

Bagian ini menjelaskan alternatif untuk API Children (dengan huruf kapital C) yang diimpor seperti berikut ini:

import { Children } from 'react';

Jangan tertukar dengan penggunaan prop children (huruf kecil c), yang merupakan hal yang baik dan dianjurkan.

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.