Как вернуть несколько строк JSX в другой оператор возврата в React?

100

Одна линия отлично работает

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

не для нескольких строк

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

Спасибо.

Шон
источник
1
Для получения дополнительной информации по этому конкретному вопросу: github.com/facebook/react/issues/2127
плюс -
не return ("asdf" "asdf");вы хотитеreturn ["asdf", "asdf"];
neaumusic

Ответы:

149

Попробуйте представить теги как вызовы функций (см. Документацию ). Тогда первый становится:

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

И второй:

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

Теперь должно быть ясно, что второй фрагмент на самом деле не имеет смысла (вы не можете вернуть более одного значения в JS). Вам нужно либо обернуть его в другой элемент (скорее всего, то, что вы хотите, таким образом вы также можете предоставить допустимое keyсвойство), либо вы можете использовать что-то вроде этого:

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

С сахаром JSX:

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

Вам не нужно сглаживать полученный массив, React сделает это за вас. См. Следующую скрипку http://jsfiddle.net/mEB2V/1/ . Опять же: объединение двух элементов в div / section, скорее всего, будет лучше в долгосрочной перспективе.

Ян Олаф Кремс
источник
4
Да, на самом деле четко задокументировано facebook.github.io/react/tips/…
Шон
1
Использование return ([...]) без бита сглаживания дает мне разметку в точности так, как я хотел, хотя возвращенный массив не должен был быть плоским, но в моем конкретном случае это не влияет на окончательный результат. Либо это?
Шон
Спасибо! ТИЛЬ! Обновляю свой ответ, чтобы включить ссылку на JSFiddle, которая показывает, что flatten не является обязательным. Также будет включена ссылка на документы React.
Ян Олаф Кремс
13
Это больше не работает (по состоянию на 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. Можно вернуть массив как часть отрисовки компонента, например <div>{ this.props.foos.map(function() { return <Foo /> }) }</div>. Но renderфункция компонента не может вернуть этот массив, не обернув его, например, в div.
Henrik N
35

Похоже, старый ответ о возврате массива больше не применяется (возможно, с React ~ 0.9, как написал @ dogmatic69 в комментарии ).

В документах говорится, что вам нужно вернуть один узел:

Максимальное количество корневых узлов JSX

В настоящее время при рендеринге компонента вы можете вернуть только один узел; если у вас есть, скажем, список возвращаемых div, вы должны заключить свои компоненты в div, span или любой другой компонент.

Не забывайте, что JSX компилируется в обычный JS; возврат двух функций не имеет синтаксического смысла. Точно так же не помещайте более одного ребенка в тройку.

Во многих случаях вы можете просто заключить вещи в a <div>или a <span>.

В моем случае я хотел вернуть несколько <tr>s. Я завернул их в <tbody>- стол может иметь несколько тел.

РЕДАКТИРОВАТЬ: Начиная с React 16.0, возвращение массива, по-видимому, снова разрешено, если каждый элемент имеет key: https://facebook.github.io/react/blog/2017/09/26/react-v16.0.html # new-render-return-types-fragments-and-strings

РЕДАКТИРОВАТЬ: React 16.2 позволяет окружать список элементов <Fragment>…</Fragment>или даже <>…</>, если вы предпочитаете это массивом: https://blog.jmes.tech/react-fragment-and-semantic-html/

Хенрик Н
источник
3
Что вы можете сделать, если хотите вернуть несколько <li>? Предполагая, что я не могу просто обернуть все это<ul>
Банджокат
@Banjocat Боюсь , я не знаю: / Вы имеете право гнездовых списки, чтобы вы могли вернуть что - то вроде <li><ul><li>one</li><li>two</li></ul></li>, что работает в вашей ситуации. Или: обертывающий div не будет строго допустимым, но, возможно, он отлично отображается во всех соответствующих браузерах? Если попробуете, дайте нам знать.
Henrik N
1
@Banjocat ... Мне бы хотелось узнать ответ на твой вопрос получше. Может быть, вам стоит сформулировать это как обычный вопрос о стеке и посмотреть, получите ли вы другой ответ.
user1175849
@ user1175849 Может быть, тогда вы могли бы опубликовать этот вопрос :)
Henrik N
1
@HenrikN Fwiw, обертывание "подмножества" liв a spanили divне сработало для меня. Div серьезно нарушил рендеринг, и, по крайней мере, в моем случае использования span испортил CSS. 2 ¢: Попытка вернуть несколько подмножеств lis - это запах кода. Мы использовали a ulкак своего рода выпадающее меню, и я изначально хотел, чтобы многие компоненты возвращали «разделы» lis. В конце концов, было лучше поместить весь код меню в один компонент, «привязанный» к нему, ulчем вставлять элементы liиз нескольких источников. Я думаю, что это также сделало ментальную модель пользовательского интерфейса немного чище.
ruffin
13

Начиная с React v16.0.0 и далее, можно возвращать несколько элементов, заключая их вArray

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

Также в React v16.2.0 представлена ​​новая функция, React Fragmentsкоторую вы можете использовать для переноса нескольких элементов.

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

Согласно документации:

Распространенный шаблон в React - компонент, возвращающий несколько элементов. Фрагменты позволяют группировать список дочерних элементов без добавления дополнительных узлов в DOM.

Фрагменты, объявленные с явным синтаксисом, могут иметь ключи. Пример использования для этого - сопоставление коллекции с массивом фрагментов - например, для создания списка описания:

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 - единственный атрибут, который можно передать Fragment. В будущем мы можем добавить поддержку дополнительных атрибутов, таких как обработчики событий.

Шубхам Хатри
источник
7

Кроме того, вы можете захотеть вернуть несколько элементов списка в некоторой вспомогательной функции внутри компонента React. Просто верните массив узлов html с keyатрибутом:

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

Вы можете использовать createFragmentздесь.

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

(здесь используется синтаксис ES6 и JSX)

вам сначала нужно добавить react-addons-create-fragmentпакет:

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

Преимущество перед решением Яна Олафа Кремса: React не жалуется на отсутствие key

сильвина
источник
поправьте меня, если я ошибаюсь, но вы можете просто добавить ключи вручную. используя пример Яна: первый элемент массива получает, например, <h3 key = {i}> </h3>, а второй элемент массива sth немного отличается, например, <p> key = {i + '-foo'}> </p>
nerdess
2

Обновлено

Используйте React Fragment. Это просто. Ссылка на фрагмент документации.

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

Старый ответ - устаревший

С React> 16 вы можете использовать react-Composite .

import { Composite } from 'react-composite';

// ...

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

Конечно, необходимо установить реакт-композит.

npm install react-composite --save
Г-н Бр
источник
0

Это просто по фрагменту React <></>и React.Fragment:

return (
    <>
      {[1, 2, 3].map(
        (n, index): ReactElement => (
          <React.Fragment key={index}>
            <h3>Item {n}</h3>
            <p>Description {n}</p>
          </React.Fragment>
        ),
      )}
    </>
  );
Масих Джахангири
источник