Как визуализировать элементы-близнецы, не помещая их в родительский тег?


82

В большинстве случаев наличие родительского тега не является проблемой.

React.createClass({
    render: function() {
        return (
            <tbody>
                <tr><td>Item 1</td></tr>
                <tr><td>Item 2</td></tr>
            </tbody>
        );
    }
});

Но в некоторых случаях имеет смысл иметь элементы-братья в одной функции рендеринга без родительского элемента, и особенно в случае таблицы вы не хотите заключать строку таблицы в файл div.

React.createClass({
    render: function() {
        return (
            <tr><td>Item 1</td></tr>
            <tr><td>Item 2</td></tr>
        );
    }
});

Второй пример дает следующее сообщение об ошибке: Adjacent XJS elements must be wrapped in an enclosing tag while parsing file.

Как я могу визуализировать два родственных элемента, не заключая их в a <div>или что-то подобное?


3
Мне всегда было сложно найти этот вопрос / ответ, поэтому я решил задать его / ответить сам. Дополнительные объяснения / ответы приветствуются.
thealexbaron

Другой пример - элементы навигации
Tjorriemorrie

Ответы:


66

В настоящее время это ограничение, но, скорее всего, в будущем оно будет исправлено ( в репозитории github есть некоторые открытые проблемы ).

На данный момент вы можете использовать функцию, которая возвращает массив (это в основном компонент без состояния):

function things(arg, onWhatever){
    return [
        <tr><td>Item 1</td></tr>,
        <tr><td>Item 2</td></tr>
    ];
}

И используйте это в своем компоненте.

return (
    <table><tbody>
      {things(arg1, this.handleWhatever)}
      {things(arg2, this.handleWhatever)}
    </tbody></table>
);

Обновить

В React 16 вы сможете вернуть массив из рендера.

Другое обновление

Теперь вы можете либо вернуть массив верхнего уровня, либо использовать <React.Fragment>.

В случае с массивом нам нужно разместить ключ для каждого элемента, поскольку React не знает, что эти два элемента являются постоянными, а не динамически созданным списком:

function RowPair() {
  return [
    <tr key="first"><td>First</td></tr>,
    <tr key="second"><td>Second</td></tr>,
  ]
}

With React.Fragment, он ведет себя больше как упаковка его в <div>или аналогичный, где keyне требуется, если мы не создаем дочерние элементы динамически. Во-первых, мы можем обернуть массив во фрагмент:

function RowPair() {
  return <React.Fragment>{[
    <tr key="first"><td>First</td></tr>,
    <tr key="second"><td>Second</td></tr>,
  ]}</React.Fragment>
}

И тогда мы можем полностью исключить массив и keys:

function RowPair() {
  return <React.Fragment>
    <tr><td>First</td></tr>
    <tr><td>Second</td></tr>
  </React.Fragment>
}

2
Почему все говорят, что это работает ???? Неперехваченная ошибка: x.render (): должен быть возвращен действительный элемент React (или null). Возможно, вы вернули undefined, массив или другой недопустимый объект.
Xogle 05

@Xogle Что-то изменилось в React API. Кажется, больше не работает.
Петр Пеллер

1
@FakeRainBrigand Я вижу проблему. Это не «компонент без состояния», это функция, возвращающая массив. Компоненты без сохранения состояния не могут возвращать массивы, и вы не можете подключить простую функцию к хранилищу Redux.
Петр Пеллер

@PetrPeller вы используете jsx? В этом примере я называю это функцией.
Brigand

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

33

Я знаю, что это старый пост, но, возможно, мой ответ может помочь новичкам вроде меня.

В React 16.2 улучшена поддержка была добавлена фрагментов .

Теперь вы можете вернуть его так:

return (
  <>
    <tr><td>Item 1</td></tr>
    <tr><td>Item 2</td></tr>
  </>
);

Вы можете обернуть его <></> или <Fragment></Fragment>.

Если вы хотите передать некоторые атрибуты, на момент написания поддерживается только ключ , и вам придется использовать<Fragment /> поскольку короткий синтаксис <></>не принимает атрибуты.

Примечание. Если вы собираетесь использовать короткий синтаксис, убедитесь, что вы используете Babel 7. .

Ссылка на источник


1
Именно то, что мне было нужно, спасибо! Кроме того, на всякий случай, если это понадобится кому-то еще: импортируйте React, {Fragment} из 'react'
Крис

Я не могу найти реализацию Fragmentв их репозитории, может кто-нибудь, пожалуйста, покажите мне? Спасибо :)
Рамон Бальтазар

Я не могу заставить его работать, даже если я установлю Babel-cli ... даже только для решения <>. Как это возможно @onoya Я скоро опубликую ветку,
Гаэль

16

Woohoo! Команда React наконец-то добавила эту функцию. Начиная с React v16.0, вы можете:

render() {
  // No need to wrap list items in an extra element!
  return [
    // Don't forget the keys :)
      <tr key="a"><td>Item 1</td></tr>,
      <tr key="b"><td>Item 2</td></tr>
  ];
}

См. Полную запись в блоге, объясняющую «Новые типы возвращаемых результатов рендеринга: фрагменты и строки» .


3

Наличие родительского элемента полезно в большинстве случаев, например, у вас может быть родительский className который может ориентироваться на стиль дочерних элементов и несколько других сценариев ...

Но если вы действительно не хотите этого делать, вы можете использовать React.Fragment

Так что просто сделайте что-нибудь вроде этого:

<React.Fragment>
  <First />
  <Second />
  <Third />
</React.Fragment>

Начиная с версии 16.2 , также используется сокращенная версия <>, которая в функции рендеринга выглядит так:

render() {
  return (
    <>
      <First />
      <Second />
      <Third />
    </>
  );
}

Кроме того, при использовании версии 16.0 и выше вы можете вернуть массив элементов, который не нуждается в родительской оболочке, как показано ниже:

render() {
 return [
  <h1 key="heading">Hello from Alireza!</h1>,
  <p key="first">Here where I'm!</p>,
  <p key="second">And again here :)</p>
 ];
}

0

Вы можете заключить его в скобки следующим образом:

React.createClass({
    render: function() {
        return (
          [
            <tr><td>Item 1</td></tr>
            <tr><td>Item 2</td></tr>
          ]
        );
    }
});

Как насчет того, если вам также нужно использовать Object.keys()в возврате?
Юха Унтинен

Например. return ( [ <tr><td>Header</td></tr>{ Object.keys(this.props.data).map(function(item) { <tr><td>{data}</td></tr> }) } ] ); С 15.4.2 он уступает Unexpected token, expected ,в начальной
сетке

попробуйте что-нибудь вроде этого: return ({Object.keys (this.props.data) .map (function (item) {<tr><td>{data}</td> </tr>}). unshift ({< tr> <td> Заголовок </td> </tr>})});
Виталий Андрусишин

Это не работает, по-прежнему требуется разделение <tr> -s запятой
Филипп Мунин

0

Этот пример мне подходит:

let values = [];

if (props.Values){
  values = [
    <tr key={1}>
      <td>props.Values[0].SomeValue</td>
    </tr>
  ,
    <tr key={2}>
        <td>props.Values[1].SomeValue</td>
        </tr>
    ];
}

return (
    <table className="no-border-table table">
      <tbody>
        <tr>
            <th>Some text</th>
        </tr>
        {values}
      </tbody>
    </table>
)

0

Что-то вроде этого синтаксиса у меня сработало

this.props.map((data,index)=>{return( [ <tr>....</tr>,<tr>....</tr>];)});

-2

Для тех, кто использует TypeScript, правильный синтаксис:

return [
  (
    <div>Foo</div>
  ),
  (
    <div>Bar</div>
  )
];

1
Это идентично ответу @thealexbaron и не имеет ничего общего с TypeScript
andrewarchi
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.