¿Cómo devolver varias líneas JSX en otra declaración de devolución en React?

100

Una sola línea funciona bien

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

no para varias líneas

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

Gracias.

Shawn
fuente
1
Para obtener más información sobre este problema en particular: github.com/facebook/react/issues/2127
plus-
no return ("asdf" "asdf");quieresreturn ["asdf", "asdf"];
neaumusic

Respuestas:

149

Intente pensar en las etiquetas como llamadas a funciones (ver documentos ). Entonces el primero se convierte en:

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

Y el segundo:

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

Ahora debería quedar claro que el segundo fragmento realmente no tiene sentido (no puede devolver más de un valor en JS). Debe envolverlo en otro elemento (lo más probable es que desee, de esa manera también puede proporcionar una keypropiedad válida ), o puede usar algo como esto:

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

Con azúcar JSX:

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

No necesita aplanar la matriz resultante, React lo hará por usted. Vea el siguiente violín http://jsfiddle.net/mEB2V/1/ . Nuevamente: envolver los dos elementos en un div / sección probablemente sea mejor a largo plazo.

Jan Olaf Krems
fuente
4
Sí, en realidad está claramente documentado facebook.github.io/react/tips/…
Shawn
1
Usar return ([...]) sin el bit de aplanar me da el marcado exactamente como quería, aunque la matriz devuelta no debe haber sido plana pero en mi caso particular no afecta la salida final. ¿O es eso?
Shawn
¡Gracias! ¡TIL! Actualizando mi respuesta para incluir un enlace a JSFiddle que muestra que aplanar es opcional. También incluirá el enlace a los documentos de React.
Jan Olaf Krems
13
Esto ya no funciona (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.
dogmatic69
2
@TimFletcher Está bien devolver una matriz como parte de la representación de un componente, por ejemplo <div>{ this.props.foos.map(function() { return <Foo /> }) }</div>. Pero la renderfunción del componente no puede devolver esa matriz sin envolverla, por ejemplo, en un div.
Henrik N
35

Parece que la respuesta anterior sobre devolver una matriz ya no se aplica (tal vez desde React ~ 0.9, como escribió @ dogmatic69 en un comentario ).

Los documentos dicen que necesitas devolver un solo nodo:

Número máximo de nodos raíz JSX

Actualmente, en el renderizado de un componente, solo puede devolver un nodo; si tiene, digamos, una lista de divs para devolver, debe envolver sus componentes dentro de un div, span o cualquier otro componente.

No olvide que JSX se compila en JS normal; devolver dos funciones realmente no tiene sentido sintáctico. Asimismo, no pongas a más de un niño en un ternario.

En muchos casos, simplemente puede envolver las cosas en a <div>o a <span>.

En mi caso, quería devolver varios <tr>s. Los envolví en una <tbody>- una mesa puede tener varios cuerpos.

EDITAR: A partir de React 16.0, la devolución de una matriz aparentemente está permitida nuevamente, siempre que cada elemento tenga key: https://facebook.github.io/react/blog/2017/09/26/react-v16.0.html # new-render-return-types-fragments-and-strings

EDITAR: React 16.2 le permite rodear una lista de elementos con <Fragment>…</Fragment>o incluso <>…</>, si lo prefiere a una matriz: https://blog.jmes.tech/react-fragment-and-semantic-html/

Henrik N
fuente
3
¿Qué puede hacer si desea devolver varios <li>? Suponiendo que no puedo simplemente envolverlo todo<ul>
Banjocat
@Banjocat Me temo que no lo sé: / Puedes anidar listas, por lo que podrías devolver algo como <li><ul><li>one</li><li>two</li></ul></li>si eso funciona en tu situación. O: ¿Un div de envoltura no sería estrictamente válido, pero tal vez funcione bien en todos los navegadores relevantes? Si lo prueba, avísenos.
Henrik N
1
@Banjocat ... Me encantaría saber una mejor respuesta a tu pregunta. Tal vez debería plantearlo como una pregunta regular de stackoverflow y ver si obtiene una respuesta diferente.
user1175849
@ user1175849 Tal vez podrías publicar esa pregunta entonces :)
Henrik N
1
@HenrikN Fwiw, envolviendo un "subconjunto" de lien un spano divno funcionó bien para mí. El div rompió seriamente la renderización y, al menos en mi caso de uso, el intervalo estropeó el CSS. 2 ¢: Intentar devolver varios subconjuntos de lis es un olor a código. Estábamos usando a ulcomo una especie de menú desplegable, e inicialmente quería que muchos componentes devolvieran "secciones" de lis. Al final, fue mejor poner todo el código del menú en un solo componente "anclado" en ulque calzar los correos electrónicos lide múltiples fuentes. Creo que también hizo que el modelo mental de la interfaz de usuario fuera un poco más limpio.
ruffin
13

Desde React v16.0.0 en adelante, es posible Devolver múltiples elementos envolviéndolos dentro de unArray

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

También desde React v16.2.0 , React Fragmentsse introduce una nueva característica llamada que puede usar para envolver múltiples 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>
      )
    }}
  );
}

Según la documentación:

Un patrón común en React es que un componente devuelva varios elementos. Los fragmentos le permiten agrupar una lista de niños sin agregar nodos adicionales al DOM.

Los fragmentos declarados con la sintaxis explícita pueden tener claves. Un caso de uso para esto es mapear una colección a una matriz de fragmentos, por ejemplo, para crear una lista de descripción:

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 es el único atributo que se puede pasar a Fragment. En el futuro, es posible que agreguemos soporte para atributos adicionales, como controladores de eventos.

Shubham Khatri
fuente
7

Además, es posible que desee devolver varios elementos de la lista en alguna función auxiliar dentro de un componente React. Simplemente devuelva una matriz de nodos html con el 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
fuente
2

Puede utilizar createFragmentaquí.

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 la sintaxis ES6 y JSX aquí)

primero tienes que agregar el react-addons-create-fragmentpaquete:

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

Ventaja sobre la solución de Jan Olaf Krems: reaccionar no se queja de los desaparecidos key

Sylvain
fuente
corríjame si me equivoco, pero simplemente puede agregar las claves manualmente. usando el ejemplo de jan: el primer elemento de la matriz obtiene, por ejemplo, <h3 key = {i}> </h3> y el segundo elemento de la matriz es algo diferente como <p> key = {i + '-foo'}> </p>
nerdess
2

Actualizado

Utilice React Fragment. Es sencillo. Enlace a la documentación del fragmento.

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

Respuesta antigua: obsoleta

Con React> 16 puedes usar react-composite .

import { Composite } from 'react-composite';

// ...

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

Por supuesto, debe instalarse react-composite.

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

Es simple por React fragment <></>y 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
fuente