Atención

Usando Children es poco común y puede conducir a un código frágil. Ver alternativas comunes.

Children te permite manipular y transformar el JSX que recibes como el children prop.

const mappedChildren = Children.map(children, child =>
<div className="Row">
{child}
</div>
);

Uso

Transformar children

Para transformar el JSX de tu componente que recibe como el children prop, llama Children.map:

import { Children } from 'react';

function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}

En el ejemplo anterior, el RowList envuelve cada child que recibe un <div className="Row> contenedor. Por ejemplo, digamos que el componente padre pasa tres etiquetas <p> como el children prop a RowList:

<RowList>
<p>Este es el primer elemento.</p>
<p>Este es el segundo elemento.</p>
<p>Este es el tercer elemento.</p>
</RowList>

Después, con la implementación anterior del RowList, el resultado final renderizado se verá así:

<div className="RowList">
<div className="Row">
<p>Este es el primer elemento.</p>
</div>
<div className="Row">
<p>Este es el segundo elemento.</p>
</div>
<div className="Row">
<p>Este es el tercer elemento.</p>
</div>
</div>

Children.map es similar a la transformación de arrays con map(). La diferencia es que el children se considera la estructura de datos opaque. Esto significa que incluso si a veces es un array, no debe asumir que es un array o cualquier otro tipo de datos en particular. Esta es la razón por la que debes usar Children.map si necesitas transformarlo.

import { Children } from 'react';

export default function RowList({ children }) {
  return (
    <div className="RowList">
      {Children.map(children, child =>
        <div className="Row">
          {child}
        </div>
      )}
    </div>
  );
}

Deep Dive

Porqué el children prop no siempre es un array?

En React, el children prop es considerado una estructura de datos opaque. Esto significa que no debe confiar en cómo está estructurado. Para transformar, filtrar, o contar children, deberías usar los métodos Children.

En la practica, la estructura de datos children a menudo se representa como un array internamente. Sin embargo, Si solo hay un child, entonces React no creará un array extra ya que esto conduciría a una sobrecarga de memoria innecesaria. Siempre y cuando use los métodos Children en lugar de hacer una introspección directa de los children prop, tú código no se romperá incluso si React cambia la forma en que se implementa realmente la estructura de datos.

Incluso cuando children es un array, Children.map tiene un comportamiento especial útil. Por ejemplo, Children.map combina las keys en los elementos devueltos en las keys del children que has pasado a ella. Esto asegura que los JSX children originales no “pierdan” las keys incluso si se envuelven como en el ejemplo anterior.

Atención

La estructura de datos children no incluye el output renderizado de los componentes que pasas como JSX. En el siguiente ejemplo, El children recibido por el RowList solo contiene dos elementos en lugar de tres:

  1. <p>Este es el primer elemento</p>
  2. <MoreRows />

Esta es la razón por la que solo se generan dos contenedores de fila en este ejemplo:

import RowList from './RowList.js';

export default function App() {
  return (
    <RowList>
      <p>Este es el primer elemento.</p>
      <MoreRows />
    </RowList>
  );
}

function MoreRows() {
  return (
    <>
      <p>Este es el segundo elemento.</p>
      <p>Este es el tercer elemento.</p>
    </>
  );
}

No hay forma de obtener el output renderizado de un componente interno como <MoreRows /> al manipular children. Esta es la razón por normalmente es mejor usar una de las soluciones alternativas.


Corriendo un código para cada child

Llama Children.forEach para iterar sobre cada child en la estructura de datos children. No devuelve ningún valor y es similar al método array forEach. Puede usarlo para ejecutar una lógica personalizada como construir su propio array.

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(); // Remueve el ultimo separator
  return result;
}

Atención

Como se mencionó anteriormente, no hay forma de obtener el output renderizado de un componente interno al manipular children. Esta es la razón por normalmente es mejor usar una de las soluciones alternativas.


Contar children

Llama Children.count(children) para calcular el número de children.

import { Children } from 'react';

export default function RowList({ children }) {
  return (
    <div className="RowList">
      <h1 className="RowListHeader">
        Filas totales: {Children.count(children)}
      </h1>
      {Children.map(children, child =>
        <div className="Row">
          {child}
        </div>
      )}
    </div>
  );
}

Atención

Como se mencionó anteriormente, no hay forma de obtener el output renderizado de un componente interno al manipular children. Esta es la razón por normalmente es mejor usar una de las soluciones alternativas.


Convertir children a un array

Llama Children.toArray(children) para convertir la estructura de datos children en un array de JavaScript regular. Esto le permite manipular el array con métodos de array integrados como filter, sort, ó reverse.

import { Children } from 'react';

export default function ReversedList({ children }) {
  const result = Children.toArray(children);
  result.reverse();
  return result;
}

Atención

Como se mencionó anteriormente, no hay forma de obtener el output renderizado de un componente interno al manipular children. Esta es la razón por normalmente es mejor usar una de las soluciones alternativas.


Alternativas

Nota

En esta sección se describen alternativas a la API Children (con C mayúscula) eso es importado asi:

import { Children } from 'react';

No lo confundas con Uso de children prop (c minúscula), lo cual es bueno y alentador.

Exponer varios componentes

Manipular children con los métodos Children a menudo conduce a un código frágil. Cuando tú pasas un children a un componente en JSX, por lo general, no espera que el componente manipule o transforme al children individualmente.

Cuando pueda, trate de evitar el uso de los métodos Children. Por ejemplo, si quieres que cada child de RowList este envuelto en <div className="Row">, exporte un componente Row y envuelve manualmente cada Row dentro de él de esta manera:

import { RowList, Row } from './RowList.js';

export default function App() {
  return (
    <RowList>
      <Row>
        <p>Este es el primer elemento.</p>
      </Row>
      <Row>
        <p>Este es el segundo elemento.</p>
      </Row>
      <Row>
        <p>Este es el tercer elemento.</p>
      </Row>
    </RowList>
  );
}

A diferencia de usar Children.map, este enfoque no envuelve a todos los child automáticamente. Sin embargo, este enfoque tiene un beneficio significativo en comparación con el ejemplo anterior con Children.map porque funciona incluso si sigues extrayendo más componentes. Por ejemplo, todavía funciona si extrae su propio componente MoreRows:

import { RowList, Row } from './RowList.js';

export default function App() {
  return (
    <RowList>
      <Row>
        <p>Este es el primer elemento.</p>
      </Row>
      <MoreRows />
    </RowList>
  );
}

function MoreRows() {
  return (
    <>
      <Row>
        <p>Este es el segundo elemento.</p>
      </Row>
      <Row>
        <p>Este es el tercer elemento.</p>
      </Row>
    </>
  );
}

Esto no funcionaría con Children.map porque “vería” <MoreRows /> como un solo child (y un solo row).


Aceptar un array de objetos como prop

También puede pasar explícitamente un array como prop. Por ejemplo, este RowList acepta el array rows como un prop :

import { RowList, Row } from './RowList.js';

export default function App() {
  return (
    <RowList rows={[
      { id: 'first', content: <p>Este es el primer elemento.</p> },
      { id: 'second', content: <p>Este es el segundo elemento.</p> },
      { id: 'third', content: <p>Este es el tercer elemento.</p> }
    ]} />
  );
}

Ya que rows es un array regular de JavaScript, el componente RowList puede usar métodos de matriz incorporados como map en el.

Este patrón es especialmente útil cuando desea poder pasar más información como datos estructurados junto con un children. En el siguiente ejemplo, el componente TabSwitcher recibe un array de objetos tabs como prop:

import TabSwitcher from './TabSwitcher.js';

export default function App() {
  return (
    <TabSwitcher tabs={[
      {
        id: 'first',
        header: 'First',
        content: <p>Este es el primer elemento.</p>
      },
      {
        id: 'second',
        header: 'Second',
        content: <p>Este es el segundo elemento.</p>
      },
      {
        id: 'third',
        header: 'Third',
        content: <p>Este es el tercer elemento.</p>
      }
    ]} />
  );
}

A diferencia de pasar el children como JSX, este enfoque le permite asociar algunos datos adicionales como header con cada elemento. Porque estás trabajando con las tabs directamente, y es una matriz, no necesita los métodos Children.


Llamar a un accesorio de representación para personalizar la representación

En lugar de producir JSX para cada artículo, también puede pasar una función que devuelve JSX, y llamar a esa función cuando sea necesario. En este ejemplo, el componente App pasa una función renderContent al componente TabSwitcher. El componente TabSwitcher llama renderContent solo para el tab seleccionado:

import TabSwitcher from './TabSwitcher.js';

export default function App() {
  return (
    <TabSwitcher
      tabIds={['primero', 'segundo', 'tercero']}
      getHeader={tabId => {
        return tabId[0].toUpperCase() + tabId.slice(1);
      }}
      renderContent={tabId => {
        return <p>Este es el {tabId} elemento.</p>;
      }}
    />
  );
}

Un prop como renderContent se llama como render prop. Porque es un prop que especifica cómo representar una parte de la interfaz de usuario. Sin embargo, no tiene nada de especial: es un prop regular que resulta ser una función.

Render props son funciones, por lo que les puedes pasar información. Por ejemplo, este componente RowList pasa el id y el index por cada fila del render prop renderRow, que usa index para resaltar las filas pares:

import { RowList, Row } from './RowList.js';

export default function App() {
  return (
    <RowList
      rowIds={['primero', 'segundo', 'tercero']}
      renderRow={(id, index) => {
        return (
          <Row isHighlighted={index % 2 === 0}>
            <p>Este es el {id} elemento.</p>
          </Row> 
        );
      }}
    />
  );
}

Este es otro ejemplo de cómo los componentes padre e hijo pueden cooperar sin manipular a los children.


Referencia

Children.count(children)

Llama Children.count(children) para contar el numero children en la estructura de datos children.

import { Children } from 'react';

function RowList({ children }) {
return (
<>
<h1>Filas totales: {Children.count(children)}</h1>
...
</>
);
}

Ver más ejemplos anteriores.

Parámetros

  • children: el valor de children prop recibido por tú componente.

Regresa

El numero de nodos de estos children.

Advertencias

  • Nodos vacíos (null, undefined, y Booleans), strings, numbers, y React elements cuentan como nodos individuales. Arrays no cuentan como arreglos individuales, pero sus children si. El recorrido no va más profundo que React elements: ellos no se renderizan, y sus children no son afectados. Fragments no son afectados.

Children.forEach(children, fn, thisArg?)

Llamar Children.forEach(children, fn, thisArg?) para correr algún código por cada child en la estructura de datos children.

import { Children } from 'react';

function SeparatorList({ children }) {
const result = [];
Children.forEach(children, (child, index) => {
result.push(child);
result.push(<hr key={index} />);
});
// ...

Ver más ejemplos anteriores.

Parámetros

  • children: El valor de children prop recibido por tú componente.
  • fn: La función que desea ejecutar para cada child, similar al callback del método array forEach. Se llamará con el child como primer argumento y su index como segundo argumento. el index empieza en 0 y se incrementa por cada llamada.
  • opcional thisArg: El valor this con el que se debe llamar a la función fn. Si se omite, es undefined.

Regresa

Children.forEach regresa undefined.

Advertencias

  • Nodos vacíos (null, undefined, y Booleans), strings, numbers, y React elements cuentan como nodos individuales. Arrays no cuentan como arreglos individuales, pero sus children si. El recorrido no va más profundo que React elements: ellos no se renderizan, y sus children no son afectados. Fragments no son afectados.

Children.map(children, fn, thisArg?)

Llamar Children.map(children, fn, thisArg?) para mapear o transformar cada child en la estructura de datos children.

import { Children } from 'react';

function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}

Ver más ejemplos anteriores.

Parámetros

  • children: El valor de children prop recibido por tú componente.
  • fn: La función de mapeo, similar al callback del método array map. Se llamará con el child como primer argumento y su index como segundo argumento. el index empieza en 0 y se incrementa por cada llamada. Necesitas regresar un React node de esta función. Esto puede ser un nodo vacío (null, undefined, o un Boolean), un string, un number, un React element, o un array de otros React nodes.
  • opcional thisArg: El valor this con el que se debe llamar a la función fn. Si se omite, es undefined.

Regresa

Si children es null o undefined, regresa el mismo valor.

De lo contrario, devuelve un array plano que consta de los nodos que ha devuelto de la función fn. devuelta contendrá todos los nodos que devolvió excepto por null y undefined.

Advertencias

  • Nodos vacíos (null, undefined, y Booleans), strings, numbers, y React elements cuentan como nodos individuales. Arrays no cuentan como arreglos individuales, pero sus children si. El recorrido no va más profundo que React elements: ellos no se renderizan, y sus children no son afectados. Fragments no son afectados.

  • Si devuelve un elemento o un array de elementos con keys de fn, las keys de los elementos devueltos se combinará automáticamente con la clave del elemento original correspondiente de children. Cuando devuelves múltiples elementos de fn en una array, sus keys solo necesitan ser únicas localmente entre sí.


Children.only(children)

Llamar Children.only(children) para afirmar que children representa un solo React element.

function Box({ children }) {
const element = Children.only(children);
// ...

Parámetros

  • children: El valor de children prop recibido por tú componente.

Regresa

Si children es un elemento valido, regresa ese elemento.

De lo contrario, lanza un error.

Advertencias

  • Este método siempre se lanza si pasas un array (como el valor de retorno de Children.map) como children. En otras palabras, hace cumplir que children es un solo React element, no es que sea un array con un solo elemento.

Children.toArray(children)

Llamar Children.toArray(children) para crear un array a partir de la estructura de datos children.

import { Children } from 'react';

export default function ReversedList({ children }) {
const result = Children.toArray(children);
result.reverse();
// ...

Parámetros

  • children: El valor de children prop recibido por tú componente.

Regresa

Devuelve un array plano de elementos en children.

Advertencias

  • Nodos vacíos (null, undefined, y Booleans) se omitirán en el array devuelto. Las keys de los elementos devueltos se calcularán a partir de las keys de los elementos originales y su nivel de anidamiento y posición. Esto asegura que aplanar el array no introduzca cambios en el comportamiento.

Solución de problemas

Paso un componente personalizado, pero los métodos Children y no muestran su resultado de renderizado

Supongamos que pasa dos children a RowList como esto:

<RowList>
<p>First item</p>
<MoreRows />
</RowList>

Si haces Children.count(children) dentro de RowList, obtendrás 2. Incluso si MoreRows renderiza 10 elementos diferentes, o si vuelve null, Children.count(children)seguirá siendo 2. Desde la perspectiva de RowList, solo “ve” el JSX que ha recibido. No “ve” las partes internas del componente MoreRows.

La limitación dificulta la extracción de un componente. Por eso las alternativas son preferibles al uso de Children.