Children
permite que você manipule e transforme o JSX que você recebeu como a children
prop.
const mappedChildren = Children.map(children, child =>
<div className="Row">
{child}
</div>
);
Referência
Children.count(children)
Chame Children.count(children)
para contar o número de filhos na estrutura de dados children
.
import { Children } from 'react';
function RowList({ children }) {
return (
<>
<h1>Total de linhas: {Children.count(children)}</h1>
...
</>
);
}
Parâmetros
children
: O valor dachildren
prop recebido pelo seu componente.
Retorna
O número de nós dentro desses children
.
Ressalvas
- Nós vazios (
null
,undefined
e Booleans), strings, números e elementos React contam como nós individuais. Arrays não contam como nós individuais, mas seus filhos contam. A travessia não vai mais fundo do que elementos React: eles não são renderizados, e seus filhos não são atravessados. Fragmentos não são atravessados.
Children.forEach(children, fn, thisArg?)
Chame Children.forEach(children, fn, thisArg?)
para executar algum código para cada filho na estrutura de dados children
.
import { Children } from 'react';
function SeparatorList({ children }) {
const result = [];
Children.forEach(children, (child, index) => {
result.push(child);
result.push(<hr key={index} />);
});
// ...
Parâmetros
children
: O valor dachildren
prop recebido pelo seu componente.fn
: A função que você deseja executar para cada filho, semelhante ao callback do métodoforEach
de array. Ela será chamada com o filho como o primeiro argumento e seu índice como o segundo argumento. O índice começa em0
e incrementa a cada chamada.- opcional
thisArg
: Othis
value com o qual a funçãofn
deve ser chamada. Se omitido, éundefined
.
Retorna
Children.forEach
retorna undefined
.
Ressalvas
- Nós vazios (
null
,undefined
e Booleans), strings, números e elementos React contam como nós individuais. Arrays não contam como nós individuais, mas seus filhos contam. A travessia não vai mais fundo do que elementos React: eles não são renderizados, e seus filhos não são atravessados. Fragmentos não são atravessados.
Children.map(children, fn, thisArg?)
Chame Children.map(children, fn, thisArg?)
para mapear ou transformar cada filho na estrutura de dados children
.
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}
Parâmetros
children
: O valor dachildren
prop recebido pelo seu componente.fn
: A função de mapeamento, semelhante ao callback do métodomap
de array. Ela será chamada com o filho como o primeiro argumento e seu índice como o segundo argumento. O índice começa em0
e incrementa a cada chamada. Você precisa retornar um nó React desta função. Isso pode ser um nó vazio (null
,undefined
ou um Boolean), uma string, um número, um elemento React ou um array de outros nós React.- opcional
thisArg
: Othis
value com o qual a funçãofn
deve ser chamada. Se omitido, éundefined
.
Retorna
Se children
é null
ou undefined
, retorna o mesmo valor.
Caso contrário, retorna um array plano consistindo dos nós que você retornou da função fn
. O array retornado conterá todos os nós que você retornou, exceto null
e undefined
.
Ressalvas
-
Nós vazios (
null
,undefined
e Booleans), strings, números e elementos React contam como nós individuais. Arrays não contam como nós individuais, mas seus filhos contam. A travessia não vai mais fundo do que elementos React: eles não são renderizados, e seus filhos não são atravessados. Fragmentos não são atravessados. -
Se você retornar um elemento ou um array de elementos com chaves de
fn
, as chaves dos elementos retornados serão automaticamente combinadas com a chave do item original correspondente dechildren
. Quando você retorna múltiplos elementos defn
em um array, suas chaves só precisam ser únicas localmente entre si.
Children.only(children)
Chame Children.only(children)
para afirmar que children
representa um único elemento React.
function Box({ children }) {
const element = Children.only(children);
// ...
Parâmetros
children
: O valor dachildren
prop recebido pelo seu componente.
Retorna
Se children
é um elemento válido, retorna esse elemento.
Caso contrário, lança um erro.
Ressalvas
- Este método sempre lança uma exceção se você passar um array (como o valor de retorno de
Children.map
) comochildren
. Em outras palavras, ele impõe quechildren
seja um único elemento React, não que seja um array com um único elemento.
Children.toArray(children)
Chame Children.toArray(children)
para criar um array a partir da estrutura de dados children
.
import { Children } from 'react';
export default function ReversedList({ children }) {
const result = Children.toArray(children);
result.reverse();
// ...
Parâmetros
children
: O valor dachildren
prop recebido pelo seu componente.
Retorna
Retorna um array plano de elementos em children
.
Ressalvas
- Nós vazios (
null
,undefined
e Booleans) serão omitidos no array retornado. As chaves dos elementos retornados serão calculadas a partir das chaves dos elementos originais e seu nível de aninhamento e posição. Isso garante que achatar o array não introduza mudanças no comportamento.
Uso
Transformando filhos
Para transformar o JSX de filhos que seu componente recebe como a children
prop, chame Children.map
:
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}
No exemplo acima, o RowList
envolve cada filho que recebe em um contêiner <div className="Row">
. Por exemplo, digamos que o componente pai passe três tags <p>
como a children
prop para RowList
:
<RowList>
<p>Este é o primeiro item.</p>
<p>Este é o segundo item.</p>
<p>Este é o terceiro item.</p>
</RowList>
Então, com a implementação do RowList
acima, o resultado renderizado final ficará assim:
<div className="RowList">
<div className="Row">
<p>Este é o primeiro item.</p>
</div>
<div className="Row">
<p>Este é o segundo item.</p>
</div>
<div className="Row">
<p>Este é o terceiro item.</p>
</div>
</div>
Children.map
é semelhante a transformar arrays com map()
. A diferença é que a estrutura de dados children
é considerada opaca. Isso significa que mesmo que às vezes seja um array, você não deve assumir que é um array ou qualquer outro tipo de dado específico. Por isso, você deve usar Children.map
se precisar transformá-lo.
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
No React, a prop children
é considerada uma estrutura de dados opaca. Isso significa que você não deve depender de como ela está estruturada. Para transformar, filtrar ou contar filhos, você deve usar os métodos Children
.
Na prática, a estrutura de dados children
é frequentemente representada como um array internamente. No entanto, se houver apenas um único filho, o React não criará um array extra, pois isso levaria a uma sobrecarga desnecessária de memória. Enquanto você usar os métodos Children
em vez de introspectar diretamente a prop children
, seu código não quebrará mesmo que o React mude como a estrutura de dados é realmente implementada.
Mesmo quando children
é um array, Children.map
tem um comportamento especial útil. Por exemplo, Children.map
combina as chaves nos elementos retornados com as chaves nos children
que você passou para ele. Isso garante que os filhos JSX originais não “percam” chaves, mesmo que sejam envoltos como no exemplo acima.
Executando algum código para cada filho
Chame Children.forEach
para iterar sobre cada filho na estrutura de dados children
. Ele não retorna nenhum valor e é semelhante ao método forEach
de array. Você pode usá-lo para executar lógica personalizada, como construir seu próprio 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(); // Remove o último separador return result; }
import { Children } from 'react'; export default function RowList({ children }) { return ( <div className="RowList"> <h1 className="RowListHeader"> Total de linhas: {Children.count(children)} </h1> {Children.map(children, child => <div className="Row"> {child} </div> )} </div> ); }
Convertendo filhos em um array
Chame Children.toArray(children)
para transformar a estrutura de dados children
em um array JavaScript regular. Isso permite que você manipule o array com métodos de array incorporados como filter
, sort
ou reverse
.
import { Children } from 'react'; export default function ReversedList({ children }) { const result = Children.toArray(children); result.reverse(); return result; }
Alternativas
Expondo múltiplos componentes
Manipular filhos com os métodos Children
muitas vezes leva a código frágil. Quando você passa filhos para um componente em JSX, geralmente não espera que o componente manipule ou transforme os filhos individuais.
Quando puder, tente evitar usar os métodos Children
. Por exemplo, se você quiser que cada filho de RowList
seja envolvido em <div className="Row">
, exporte um componente Row
e envolva manualmente cada linha assim:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList> <Row> <p>Este é o primeiro item.</p> </Row> <Row> <p>Este é o segundo item.</p> </Row> <Row> <p>Este é o terceiro item.</p> </Row> </RowList> ); }
Diferente de usar Children.map
, essa abordagem não envolve automaticamente cada filho. No entanto, essa abordagem tem um benefício significativo comparado ao exemplo anterior com Children.map
porque funciona mesmo se você continuar extraindo mais componentes. Por exemplo, ainda funciona se você extrair seu próprio componente MoreRows
:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList> <Row> <p>Este é o primeiro item.</p> </Row> <MoreRows /> </RowList> ); } function MoreRows() { return ( <> <Row> <p>Este é o segundo item.</p> </Row> <Row> <p>Este é o terceiro item.</p> </Row> </> ); }
Isso não funcionaria com Children.map
porque “veria” <MoreRows />
como um filho único (e uma única linha).
Aceitando um array de objetos como uma prop
Você também pode passar explicitamente um array como uma prop. Por exemplo, este RowList
aceita um array rows
como uma prop:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList rows={[ { id: 'first', content: <p>Este é o primeiro item.</p> }, { id: 'second', content: <p>Este é o segundo item.</p> }, { id: 'third', content: <p>Este é o terceiro item.</p> } ]} /> ); }
Como rows
é um array JavaScript regular, o componente RowList
pode usar métodos de array incorporados como map
nele.
Esse padrão é especialmente útil quando você deseja poder passar mais informações como dados estruturados junto com os filhos. No exemplo abaixo, o componente TabSwitcher
recebe um array de objetos como a prop tabs
:
import TabSwitcher from './TabSwitcher.js'; export default function App() { return ( <TabSwitcher tabs={[ { id: 'first', header: 'Primeiro', content: <p>Este é o primeiro item.</p> }, { id: 'second', header: 'Segundo', content: <p>Este é o segundo item.</p> }, { id: 'third', header: 'Terceiro', content: <p>Este é o terceiro item.</p> } ]} /> ); }
Diferente de passar os filhos como JSX, essa abordagem permite que você associe alguns dados extras como header
com cada item. Como você está trabalhando diretamente com os tabs
, e este é um array, você não precisa dos métodos Children
.
Chamando uma função de renderização para personalizar a renderização
Em vez de produzir JSX para cada item único, você também pode passar uma função que retorna JSX e chamar essa função quando necessário. Neste exemplo, o componente App
passa uma função renderContent
para o componente TabSwitcher
. O componente TabSwitcher
chama renderContent
apenas para a aba selecionada:
import TabSwitcher from './TabSwitcher.js'; export default function App() { return ( <TabSwitcher tabIds={['first', 'second', 'third']} getHeader={tabId => { return tabId[0].toUpperCase() + tabId.slice(1); }} renderContent={tabId => { return <p>Este é o item {tabId}.</p>; }} /> ); }
Uma prop como renderContent
é chamada de render prop porque é uma prop que especifica como renderizar uma parte da interface do usuário. No entanto, não há nada de especial sobre isso: é uma prop regular que acontece de ser uma função.
Render props são funções, então você pode passar informações para elas. Por exemplo, este componente RowList
passa o id
e o index
de cada linha para o render prop renderRow
, que usa index
para destacar linhas pares:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList rowIds={['first', 'second', 'third']} renderRow={(id, index) => { return ( <Row isHighlighted={index % 2 === 0}> <p>Este é o item {id}.</p> </Row> ); }} /> ); }
Este é outro exemplo de como componentes pai e filho podem cooperar sem manipular os filhos.
Solução de Problemas
Eu passo um componente personalizado, mas os métodos Children
não mostram seu resultado de renderização
Suponha que você passe dois filhos para RowList
assim:
<RowList>
<p>Primeiro item</p>
<MoreRows />
</RowList>
Se você fizer Children.count(children)
dentro de RowList
, obterá 2
. Mesmo que MoreRows
renderize 10 itens diferentes, ou se retornar null
, Children.count(children)
ainda será 2
. Do ponto de vista do RowList
, ele só “vê” o JSX que recebeu. Ele não “vê” os internos do componente MoreRows
.
A limitação torna difícil extrair um componente. É por isso que soluções alternativas são preferidas ao usar Children
.