Como retornar várias linhas JSX em outra instrução de retorno no React?

100

Uma linha funciona bem

render: function () {
  return (
    {[1,2,3].map(function (n) {
      return <p>{n}</p>
    }}
  );
}

não para várias linhas

render: function () {
  return (
    {[1,2,3].map(function (n) {
      return (
        <h3>Item {n}</h3>
        <p>Description {n}</p>
      )
    }}
  );
}

Obrigado.

Shawn
fonte
1
Para mais informações sobre este problema específico: github.com/facebook/react/issues/2127
plus-
return ("asdf" "asdf");você não querreturn ["asdf", "asdf"];
neaumusic

Respostas:

149

Tente pensar nas tags como chamadas de função (consulte a documentação ). Então o primeiro se torna:

{[1,2,3].map(function (n) {
  return React.DOM.p(...);
})}

E o segundo:

{[1,2,3].map(function (n) {
  return (
    React.DOM.h3(...)
    React.DOM.p(...)
  )
})}

Agora deve estar claro que o segundo snippet realmente não faz sentido (você não pode retornar mais de um valor em JS). Você deve envolvê-lo em outro elemento (provavelmente o que você deseja, dessa forma, você também pode fornecer uma keypropriedade válida ) ou pode usar algo assim:

{[1,2,3].map(function (n) {
  return ([
    React.DOM.h3(...),
    React.DOM.p(...)
  ]);
})}

Com açúcar JSX:

{[1,2,3].map(function (n) {
  return ([
    <h3></h3>, // note the comma
    <p></p>
  ]);
})}

Você não precisa nivelar o array resultante, o React fará isso para você. Veja o seguinte violino http://jsfiddle.net/mEB2V/1/ . Novamente: agrupar os dois elementos em uma div / seção provavelmente será melhor a longo prazo.

Jan Olaf Krems
fonte
4
Sim, na verdade claramente documentado facebook.github.io/react/tips/…
Shawn
1
Usar return ([...]) sem o bit flatten me dá a marcação exatamente como eu queria, embora o array retornado não deva ter sido flattend, mas no meu caso particular isso não afeta a saída final. Ou é?
Shawn
Obrigado! TIL! Atualizar minha resposta para incluir um link para um JSFiddle que mostra que nivelar é opcional. Também incluirá o link para os documentos do React.
Jan Olaf Krems
13
Isso não funciona mais (a partir de 0.9ish)Uncaught Error: Invariant Violation: Product.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object.
dogmático69
2
@TimFletcher Não há problema em retornar um array como parte da renderização de um componente, por exemplo <div>{ this.props.foos.map(function() { return <Foo /> }) }</div>. Mas a renderfunção do componente não pode retornar esse array sem envolvê-lo, por exemplo, em um div.
Henrik N
35

Parece que a velha resposta sobre retornar um array não se aplica mais (talvez desde React ~ 0.9, como @ dogmatic69 escreveu em um comentário ).

Os documentos dizem que você precisa retornar um único nó:

Número máximo de nós raiz JSX

Atualmente, na renderização de um componente, você só pode retornar um nó; se você tem, digamos, uma lista de divs para retornar, você deve agrupar seus componentes dentro de um div, span ou qualquer outro componente.

Não se esqueça de que JSX compila em JS regular; retornar duas funções realmente não faz sentido sintático. Da mesma forma, não coloque mais de uma criança em um ternário.

Em muitos casos, você pode simplesmente embrulhar as coisas em um <div>ou a <span>.

No meu caso, eu queria retornar vários <tr>s. Eu os embrulhei em uma <tbody>- uma mesa pode ter vários corpos.

EDITAR: a partir do React 16.0, retornar uma matriz aparentemente é permitido novamente, desde que cada elemento tenha um key: https://facebook.github.io/react/blog/2017/09/26/react-v16.0.html # new-render-return-types-fragments-and-strings

EDIT: React 16.2 permite cercar uma lista de elementos com <Fragment>…</Fragment>ou mesmo <>…</>, se você preferir isso a um array: https://blog.jmes.tech/react-fragment-and-semantic-html/

Henrik N
fonte
3
O que você pode fazer se quiser retornar vários <li>? Supondo que eu não possa simplesmente encerrar tudo<ul>
Banjocat
@Banjocat Infelizmente, não sei: / Você tem permissão para aninhar listas, portanto, pode retornar algo como <li><ul><li>one</li><li>two</li></ul></li>se isso funcionar na sua situação. Ou: Uma div envolvente não seria estritamente válida, mas talvez funcione bem em todos os navegadores relevantes? Se você tentar, avise-nos.
Henrik N de
1
@Banjocat ... Eu adoraria saber uma resposta melhor para sua pergunta. Talvez você deva colocar isso como uma pergunta stackoverflow regular e ver se obtém uma resposta diferente.
user1175849
@ user1175849 Talvez você pudesse postar essa pergunta :)
Henrik N
1
@HenrikN Fwiw, envolver um "subconjunto" de liem um spanou divnão funcionou bem para mim. O div quebrou seriamente a renderização e, pelo menos no meu caso de uso, o span bagunçou o CSS. 2 ¢: Tentar retornar vários subconjuntos de lis é um cheiro de código. Estávamos usando um ulcomo uma espécie de menu suspenso, e inicialmente queria que muitos componentes retornassem "seções" de lis. No final, era melhor colocar todo o código do menu em um único componente "ancorado" em uldo que instalar lis de fontes múltiplas. Acho que também deixou o modelo mental da IU um pouco mais limpo.
ruffin
13

Do React v16.0.0 em diante, é possível retornar vários elementos envolvendo-os dentro de umArray

render() {
  return (
    {[1,2,3].map(function (n) {
      return [
        <h3>Item {n}</h3>.
        <p>Description {n}</p>
      ]
    }}
  );
}

Também a partir do React v16.2.0 , um novo recurso chamado React Fragmentsé introduzido, que você pode usar para envolver vários elementos

render() {
  return (
    {[1,2,3].map(function (n, index) {
      return (
        <React.Fragment key={index}>
            <h3>Item {n}</h3>
            <p>Description {n}</p>
        </React.Fragment>
      )
    }}
  );
}

Conforme a documentação:

Um padrão comum no React é um componente retornar vários elementos. Fragmentos permitem agrupar uma lista de filhos sem adicionar nós extras ao DOM.

Fragmentos declarados com a sintaxe explícita podem ter chaves. Um caso de uso para isso é mapear uma coleção para uma matriz de fragmentos - por exemplo, para criar uma lista de descrição:

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        // Without the `key`, React will fire a key warning
        <React.Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
}

key é o único atributo que pode ser passado para Fragment. No futuro, podemos adicionar suporte para atributos adicionais, como manipuladores de eventos.

Shubham Khatri
fonte
7

Além disso, você pode querer retornar vários itens de lista em alguma função auxiliar dentro de um componente React. Basta retornar uma matriz de nós html com o keyatributo:

import React, { Component } from 'react'

class YourComponent extends Component {
  // ...

  render() {
    return (
      <ul>
        {this.renderListItems()}
      </ul>
    )
  }      

  renderListItems() {
    return [
      <li key={1}><a href="#">Link1</a></li>,
      <li key={2}><a href="#">Link2</a></li>,
      <li key={3} className="active">Active item</li>,
    ]
  }
}
Dmitriy
fonte
2

Você pode usar createFragmentaqui.

https://facebook.github.io/react/docs/create-fragment.html

import createFragment from 'react-addons-create-fragment';
...
{[1,2,3].map((n) => createFragment({
    h: <h3>...</h3>,
    p: <p>...</p>
  })
)}

(usando ES6 e sintaxe JSX aqui)

primeiro você precisa adicionar o react-addons-create-fragmentpacote:

npm install --save react-addons-create-fragment

Vantagem sobre a solução de Jan Olaf Krems: reagir não reclamar dos desaparecidos key

Sylvain
fonte
corrija-me se eu estiver errado, mas você pode simplesmente adicionar as chaves manualmente. usando o exemplo de jan: o primeiro item da matriz obtém, por exemplo, <h3 key = {i}> </h3> e o segundo item da matriz sth ligeiramente diferente como <p> key = {i + '-foo'}> </p>
nerdess
2

Atualizada

Use React Fragment. É simples. Link para a documentação do fragmento.

render() {
  return (
    <>
    {[1,2,3].map((value) => <div>{value}</div>)}
    </>
  );
}

Resposta antiga - obsoleta

Com React> 16, você pode usar o composto de reação .

import { Composite } from 'react-composite';

// ...

{[1,2,3].map((n) => (
  <Composite>
    <h2>Title {n}</h2>
    <p>Description {n}</p>
  </Composite>
))};

Obviamente, o composto reactivo deve ser instalado.

npm install react-composite --save
Sr. Br
fonte
0

É simples por fragmento React <></>e React.Fragment:

return (
    <>
      {[1, 2, 3].map(
        (n, index): ReactElement => (
          <React.Fragment key={index}>
            <h3>Item {n}</h3>
            <p>Description {n}</p>
          </React.Fragment>
        ),
      )}
    </>
  );
Masih Jahangiri
fonte