Come restituire più righe JSX in un'altra istruzione return in React?


100

La singola riga funziona bene

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

non per più righe

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

Grazie.


1
Per maggiori informazioni su questo particolare problema: github.com/facebook/react/issues/2127
plus-

non return ("asdf" "asdf");vuoireturn ["asdf", "asdf"];
neaumusic

Risposte:


149

Prova a pensare ai tag come a chiamate di funzioni (vedi documenti ). Quindi il primo diventa:

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

E il secondo:

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

Ora dovrebbe essere chiaro che il secondo snippet non ha davvero senso (non puoi restituire più di un valore in JS). Devi racchiuderlo in un altro elemento (molto probabilmente quello che vorresti, in questo modo puoi anche fornire una keyproprietà valida ), oppure puoi usare qualcosa del genere:

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

Con lo zucchero JSX:

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

Non è necessario appiattire l'array risultante, React lo farà per te. Vedi il seguente violino http://jsfiddle.net/mEB2V/1/ . Ancora: avvolgere i due elementi in un div / sezione sarà molto probabilmente migliore a lungo termine.


4
Sì effettivamente documentato chiaramente facebook.github.io/react/tips/…
Shawn

1
Usare return ([...]) senza il bit flatten mi dà il markup esattamente come volevo, anche se l'array restituito non deve essere stato appiattito ma nel mio caso particolare non influisce sull'output finale. O è?
Shawn

Grazie! TIL! Aggiornamento della mia risposta per includere un collegamento a un JSFiddle che mostra che flatten è facoltativo. Includerà anche il collegamento alla documentazione di React.
Jan Olaf Krems

13
Questo non funziona più (a partire da 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 Va bene restituire un array come parte del rendering di un componente, ad es <div>{ this.props.foos.map(function() { return <Foo /> }) }</div>. Ma la renderfunzione del componente non può restituire quell'array senza avvolgerlo, ad esempio in un div.
Henrik N

35

Sembra che la vecchia risposta sulla restituzione di un array non si applichi più (forse da React ~ 0.9, come ha scritto @ dogmatic69 in un commento ).

I documenti dicono che devi restituire un singolo nodo:

Numero massimo di nodi root JSX

Attualmente, nel rendering di un componente, puoi restituire solo un nodo; se hai, ad esempio, un elenco di div da restituire, devi racchiudere i tuoi componenti all'interno di un div, span o qualsiasi altro componente.

Non dimenticare che JSX si compila in JS regolare; restituire due funzioni non ha davvero senso sintattico. Allo stesso modo, non mettere più di un bambino in un ternario.

In molti casi puoi semplicemente racchiudere le cose in a <div>o a <span>.

Nel mio caso, volevo restituire più messaggi di posta <tr>elettronica. Li ho avvolti in un <tbody>- un tavolo può avere più corpi.

EDIT: A partire da React 16.0, la restituzione di un array è apparentemente nuovamente consentita, a condizione che ogni elemento abbia un 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 ti consente di racchiudere un elenco di elementi con <Fragment>…</Fragment>o anche <>…</>, se lo preferisci a un array: https://blog.jmes.tech/react-fragment-and-semantic-html/


3
Cosa puoi fare se vuoi restituire più <li>? Supponendo che non posso semplicemente concludere tutto<ul>
Banjocat

@Banjocat Temo di non saperlo: / Ti è permesso annidare liste, quindi potresti restituire qualcosa come <li><ul><li>one</li><li>two</li></ul></li>se funzionasse nella tua situazione. Oppure: un div wrapping non sarebbe strettamente valido, ma forse viene visualizzato correttamente in tutti i browser pertinenti? Se lo provi, faccelo sapere.
Henrik N

1
@Banjocat ... mi piacerebbe conoscere una risposta migliore alla tua domanda. Forse dovresti porla come una normale domanda di stackoverflow e vedere se ottieni una risposta diversa.
user1175849

@ user1175849 Forse potresti postare questa domanda allora :)
Henrik N

1
@HenrikN FWIW, avvolgendo un "sottoinsieme" di liuna spano divnon ha funzionato bene per me. Il div ha seriamente danneggiato il rendering e, almeno nel mio caso d'uso, l'intervallo ha incasinato il CSS. 2 ¢: Cercare di restituire diversi sottoinsiemi di lis è un odore di codice. Stavamo usando ulcome una sorta di menu a discesa e inizialmente volevo che molti componenti restituissero "sezioni" di lis. Alla fine, era meglio mettere tutto il codice del menu in un unico componente "ancorato" a ulpiuttosto che mettere le scarpe in lis da più sorgenti. Penso che abbia reso anche il modello mentale per l'interfaccia utente un po 'più pulito.
ruffin

13

Da React v16.0.0 in poi, è possibile restituire più elementi avvolgendoli in un fileArray

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

Sempre da React v16.2.0 , React Fragmentsviene introdotta una nuova funzionalità chiamata che puoi usare per avvolgere più elementi

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

Come da documentazione:

Un modello comune in React è che un componente restituisca più elementi. I frammenti ti consentono di raggruppare un elenco di figli senza aggiungere nodi extra al DOM.

I frammenti dichiarati con la sintassi esplicita possono avere chiavi. Un caso d'uso per questo è la mappatura di una raccolta su un array di frammenti, ad esempio per creare un elenco di descrizioni:

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 è l'unico attributo che può essere passato a Fragment. In futuro, potremmo aggiungere il supporto per attributi aggiuntivi, come i gestori di eventi.


7

Inoltre, potresti voler restituire diversi elementi della lista in qualche funzione di supporto all'interno di un componente React. Basta restituire un array di nodi html con l' keyattributo:

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>,
    ]
  }
}

2

Puoi usare createFragmentqui.

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

(utilizzando la sintassi ES6 e JSX qui)

devi prima aggiungere il react-addons-create-fragmentpacchetto:

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

Vantaggio rispetto alla soluzione di Jan Olaf Krems: reagire non si lamenta del disperso key


correggimi se sbaglio ma puoi semplicemente aggiungere le chiavi manualmente. usando l'esempio di jan: il primo elemento dell'array ottiene ad esempio <h3 key = {i}> </h3> e il secondo elemento dell'array sth leggermente diverso come <p> key = {i + '-foo'}> </p>
nerdess

2

Aggiornato

Usa React Fragment. È semplice. Collegamento alla documentazione del frammento.

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

Vecchia risposta - obsoleta

Con React> 16 puoi utilizzare il composito reattivo .

import { Composite } from 'react-composite';

// ...

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

Ovviamente, deve essere installato reattivo.

npm install react-composite --save

0

È semplice con React fragment <></>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>
        ),
      )}
    </>
  );
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.