Ошибка разбора: смежные элементы JSX должны быть заключены в тег

467

Я пытаюсь настроить свое React.jsприложение так, чтобы оно отображало только переменную, которую я установил true.

То, как настроена моя функция рендеринга, выглядит так:

render: function() {
    var text = this.state.submitted ? 'Thank you!  Expect a follow up at '+email+' soon!' : 'Enter your email to request early access:';
    var style = this.state.submitted ? {"backgroundColor": "rgba(26, 188, 156, 0.4)"} : {};
    return (
    <div>

if(this.state.submitted==false) 
{

      <input type="email" className="input_field" onChange={this._updateInputValue} ref="email" value={this.state.email} />

      <ReactCSSTransitionGroup transitionName="example" transitionAppear={true}>
      <div className="button-row">
         <a href="#" className="button" onClick={this.saveAndContinue}>Request Invite</a>
     </div>
     </ReactCSSTransitionGroup>
}
   </div>
    )
  },

По сути, важной частью здесь является if(this.state.submitted==false)часть (я хочу, чтобы эти divэлементы отображались, когда заданная переменная установлена ​​в false).

Но при запуске этого я получаю ошибку в вопросе:

Uncaught Error: Parse Error: Строка 38: Соседние элементы JSX должны быть заключены в тег

В чем здесь проблема? И что я могу использовать, чтобы сделать эту работу?

user1072337
источник
3
stackoverflow.com/questions/25034994/… Другие люди здесь просто говорят вам использовать родительский элемент, но это может быть ненужным. Эта старая версия вашего вопроса имеет интересный ответ с использованием массивов.
Мяу

Ответы:

637

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

// WRONG!

return (  
    <Comp1 />
    <Comp2 />
)

Вместо:

// Correct

return (
    <div>
       <Comp1 />
       <Comp2 />
    </div>
)

Редактировать: комментарий Per Joe Clay об API Fragments

// More Correct

return (
    <React.Fragment>
       <Comp1 />
       <Comp2 />
    </React.Fragment>
)

// Short syntax

return (
    <>
       <Comp1 />
       <Comp2 />
    </>
)
wdanxna
источник
4
Что делать, если я печатаю 2 строки вместе в таблице. Как я могу обернуть <tr>?
Хосе
235

Уже поздно отвечать на этот вопрос, но я подумал, что это добавит объяснения.

Это происходит потому, что в любом месте вашего кода вы возвращаете два элемента одновременно.

например

return(
    <div id="div1"></div>
    <div id="div1"></div>
  )

Он должен быть обернут в родительский элемент. например

 return(
      <div id="parent">
        <div id="div1"></div>
        <div id="div1"></div>
      </div>
      )


Более подробное объяснение

Ваш jsxкод ниже преобразуется

class App extends React.Component {
  render(){
    return (
      <div>
        <h1>Welcome to React</h1>
      </div>
    );
  }
}

в это

_createClass(App, [{
    key: 'render',
    value: function render() {
      return React.createElement(
        'div',
        null,
        React.createElement(
          'h1',
          null,
          'Welcome to React'
        )
      );
    }
  }]);

Но если вы сделаете это

class App extends React.Component {
  render(){
    return (
        <h1>Welcome to React</h1>
        <div>Hi</div>
    );
  }
}

это преобразуется в это (только для иллюстрации, на самом деле вы получите error : Adjacent JSX elements must be wrapped in an enclosing tag)

_createClass(App, [{
    key: 'render',
    value: function render() {
      return React.createElement(
        'div',
        null,
       'Hi'
      ); 
    return React.createElement(
          'h1',
          null,
          'Welcome to React'
        )
    }
  }]);

В приведенном выше коде вы можете видеть, что вы пытаетесь дважды вернуться из вызова метода, что, очевидно, неправильно.

Edit- Последние изменения в React 16 и собственных подопечных:

Если вы не хотите добавлять дополнительный div для переноса и хотите вернуть более одного дочернего компонента, вы можете использовать его React.Fragments.

React.Fragments немного быстрее и потребляет меньше памяти (не нужно создавать дополнительный узел DOM, меньше загроможденного дерева DOM).

например (в Реакте 16.2.0)

render() {
  return (
    <>
       React fragments.
      <h2>A heading</h2>
      More React fragments.
      <h2>Another heading</h2>
      Even more React fragments.
    </>
  );
}

или

render() {
  return (
    <React.Fragments>
       React fragments.
      <h2>A heading</h2>
      More React fragments.
      <h2>Another heading</h2>
      Even more React fragments.
    <React.Fragments/>
  );
}

или

render() {
 return [
  "Some text.",
  <h2 key="heading-1">A heading</h2>,
  "More text.",
  <h2 key="heading-2">Another heading</h2>,
  "Even more text."
 ];
}
WitVault
источник
115

Элемент React должен возвращать только один элемент. Вам придется обернуть оба тега другим тегом элемента.

Я также вижу, что ваша функция рендеринга ничего не возвращает. Вот как должен выглядеть ваш компонент:

var app = React.createClass({
    render () {
        /*React element can only return one element*/
        return (
             <div></div>
        )
    }
})

Также обратите внимание, что вы не можете использовать ifоператоры внутри возвращаемого элемента:

render: function() {
var text = this.state.submitted ? 'Thank you!  Expect a follow up at '+email+' soon!' : 'Enter your email to request early access:';
var style = this.state.submitted ? {"backgroundColor": "rgba(26, 188, 156, 0.4)"} : {};
    if(this.state.submitted==false) {
        return <YourJSX />
    } else {
        return <YourOtherJSX />
    }
},
Матан Губкин
источник
это не решает проблему с «если»; Если я удаляю «если» из функции рендера, она работает нормально.
user1072337
1
обратите внимание, что вы не можете использовать if внутри возвращенного элемента. посмотрите на мой обновленный ответ
Матан Губкин
99

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

// Wrong!
return (  
   <Comp1 />
   <Comp2 />
)

Это можно записать как:

// Correct!
return (  
    [<Comp1 />,
    <Comp2 />]
)

Обратите внимание, что выше будет выдано предупреждение: Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of 'YourComponent'.

Это можно исправить, добавив keyатрибут к компонентам, если добавить их вручную, например:

return (  
    [<Comp1 key="0" />,
    <Comp2 key="1" />]
)

Вот еще немного информации о ключах: Состав против Наследования

Нил
источник
7
Я попробовал это, и это дает мне ошибку. Действительный элемент React (или ноль) должен быть возвращен. Возможно, вы вернули undefined, массив или другой недопустимый объект.
prasadmsvs
3
@prasadmsvs +1 invariant.js: 39 Неполное нарушение инварианта: CommitFilter.render (): должен быть возвращен действительный ReactComponent. Возможно, вы вернули undefined, массив или другой недопустимый объект.
Светлана Иванова
1
Это отличное решение для тех случаев, когда вам нужно / нужно избегать элемента-обертки!
bloudermilk
2
@aaaaaa это невозможно из-за того, как работает текущий React-примиритель. Это стек, а согласование выполняется рекурсивно. В React 16 это исправлено, и теперь вы можете вернуть массив.
Natnai
1
github.com/iamdustan/tiny-react-renderer - отличный репозиторий, который должен прочитать каждый разработчик. Как только вы это сделаете, вам сразу станет ясно, почему текущая реализация реагирования не позволяет возвращать несколько детей.
Натнаи
49

Проблема

Ошибка разбора: смежные элементы JSX должны быть заключены в тег

Это означает, что вы пытаетесь вернуть несколько родственных элементов JSX неправильным образом. Помните, что вы пишете не HTML, а JSX! Ваш код переносится из JSX в JavaScript. Например:

render() {
  return (<p>foo bar</p>);
}

будет перенесено в:

render() {
  return React.createElement("p", null, "foo bar");
}

Если вы не новичок в программировании в целом, вы уже знаете, что функции / методы (любого языка) принимают любое количество параметров, но всегда возвращают только одно значение. Учитывая это, вы, вероятно, можете увидеть, что проблема возникает при попытке вернуть несколько родственных компонентов в зависимости от того, как createElement()работает; он принимает параметры только для одного элемента и возвращает его. Следовательно, мы не можем вернуть несколько элементов из одного вызова функции.


Так что, если вы когда-нибудь задумывались, почему это работает ...

render() {
  return (
    <div>
      <p>foo</p>
      <p>bar</p>
      <p>baz</p>
    </div>
  );
}

но не это ...

render() {
  return (
    <p>foo</p>
    <p>bar</p>
    <p>baz</p>
  );
}

это потому , что в первом фрагменте, оба <p>-элементов являются частью childrenв <div>-элементе. Когда они являются частью childrenтогда, мы можем выразить неограниченное количество родственных элементов. Посмотрите, как это будет происходить:

render() {
  return React.createElement(
    "div",
    null,
    React.createElement("p", null, "foo"),
    React.createElement("p", null, "bar"),
    React.createElement("p", null, "baz"),
  );
}

Решения

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

  • Используйте фрагменты (только React v16.2 +!)

    Начиная с React v16.2, React поддерживает фрагменты, которые являются компонентом без узла, который возвращает свои дочерние элементы напрямую.

    Возвращение потомков в массиве (см. Ниже) имеет некоторые недостатки:

    • Дочерние элементы в массиве должны быть разделены запятыми.
    • У дочерних элементов в массиве должен быть ключ, чтобы предотвратить предупреждение ключа React.
    • Строки должны быть заключены в кавычки.

    Они исключены из использования фрагментов. Вот пример детей, завернутых во фрагмент:

    render() {
      return (
        <>
          <ChildA />
          <ChildB />
          <ChildC />
        </>
      );
    }

    который удаляет сахар в:

    render() {
      return (
        <React.Fragment>
          <ChildA />
          <ChildB />
          <ChildC />
        </React.Fragment>
      );
    }

    Обратите внимание, что для первого фрагмента требуется Babel v7.0 или выше.


  • Вернуть массив (только React v16.0 +!)

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

    Другими словами, теперь вы можете сделать:

    render() {
      return [<p key={0}>foo</p>, <p key={1}>bar</p>];
    }

    это превращается в:

    return [React.createElement("p", {key: 0}, "foo"), React.createElement("p", {key: 1}, "bar")];

    Обратите внимание, что выше возвращает массив. Массивы являются действительными элементами React начиная с версии 16 React и более поздних. Для более ранних версий React массивы не являются допустимыми объектами возврата!

    Также обратите внимание, что следующее недопустимо (вы должны вернуть массив):

    render() {
      return (<p>foo</p> <p>bar</p>);
    }

  • Обернуть элементы в родительский элемент

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

    render() {
      return (
        <div>
          <h1>foo</h1>
          <h2>bar</h2>
        </div>
      );
    }

    Примечание. Посмотрите снова на верхнюю часть этого ответа, чтобы узнать подробности и узнать, как это происходит .

Крис
источник
@ Гривухед, не для компонентов, нет. Только для детей.
Крис
1
Хорошая, исправленная проблема для меня.
Сохан
1
Спасибо Крис, что нашли время, чтобы объяснить другие решения!
Marvel Moe
на React v16.4первый пример не работает с использованием:, <>only <React.Fragment>:(
vsync
@vsync, поддержка этого синтаксиса зависит от вашей строительной среды. Не уверен, поддерживает ли babel это, и если да, то какая версия.
Крис
22

Реакт 16.0.0 мы можем вернуть несколько компонентов рендеринга в виде массива.

return ([
    <Comp1 />,
    <Comp2 />
]);

Реагируя на 16.4.0, мы можем вернуть несколько компонентов рендера в теге Fragment. Фрагмент

return (
<React.Fragment>
    <Comp1 />
    <Comp2 />
</React.Fragment>);

Будущее реагирует, вы сможете использовать этот сокращенный синтаксис. (многие инструменты еще не поддерживают его, поэтому вы можете явно писать, <Fragment>пока инструмент не догонит.)

return (
<>
    <Comp1 />
    <Comp2 />
</>)
Моррис С
источник
1
Вы забыли ,между компонентами. Это массив, поэтому вам нужно отделить каждый элемент внутри него.
Крис
Там нет <Fragment>, это <React.Fragment>. так сказано в вашей собственной ссылке
vsync
2
Если вы деструктурируете, import React { Fragment } from 'react';то вы используете это так<Fragment >
Morris S
7

Если вы не заверните свой компонент, то вы можете написать его, как указано ниже метод.

Вместо:

return(
  <Comp1 />
  <Comp2 />
     );

Вы можете написать это:

return[(
 <Comp1 />
),
(
<Comp2 />
) ];
Ронак Ганатра
источник
6

это очень просто, мы можем использовать родительский элемент div, чтобы обернуть все элементы, или мы можем использовать концепцию компонента высшего порядка (HOC's), то есть очень полезную для приложений реагировать js

render() {
  return (
    <div>
      <div>foo</div>
      <div>bar</div>
    </div>
  );
}

или другой лучший способ - это HOC, это очень просто, не очень сложно, просто добавьте файл hoc.js в ваш проект и просто добавьте эти коды

const aux = (props) => props.children;
export default aux;

Теперь импортируйте файл hoc.js, куда вы хотите использовать, теперь вместо переноса с элементом div мы можем переносить с помощью hoc.

import React, { Component } from 'react';
import Hoc from '../../../hoc';

    render() {
      return (
    <Hoc>
        <div>foo</div>
        <div>bar</div>
    </Hoc>
      );
    }
Фазаль
источник
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, как и / или почему он решает проблему, улучшит долгосрочную ценность ответа. Прочитайте это .
Шантешвар Инд
Функциональность, о которой вы говорите, т.е. называется HOC в реагирующих. значит props.children
Fazal
4

В реакции есть правило, что у выражения JSX должен быть ровно один самый внешний элемент.

неправильно

const para = (
    <p></p>
    <p></p>
);

правильный

const para = (
    <div>
        <p></p>
        <p></p>
    </div>
);
Khandelwal-Маник
источник
1

React 16 получает ваш возврат в виде массива, поэтому он должен быть заключен в один элемент, например, div.

Неправильный подход

render(){
    return(
    <input type="text" value="" onChange={this.handleChange} />

     <button className="btn btn-primary" onClick=   {()=>this.addTodo(this.state.value)}>Submit</button>

    );
}

Правильный подход (все элементы в одном div или другом элементе, который вы используете)

render(){
    return(
        <div>
            <input type="text" value="" onChange={this.handleChange} />

            <button className="btn btn-primary" onClick={()=>this.addTodo(this.state.value)}>Submit</button>
        </div>
    );
}
Абдул Моиз
источник
1

Реактивные компоненты должны быть упакованы в один контейнер, который может быть любым тегом, например "<div> .. </ div>"

Вы можете проверить метод визуализации ReactCSSTransitionGroup

Шивпрасад П
источник
0

Импортировать вид и упаковывать View. Упаковка в divне работала для меня.

import { View } from 'react-native';
...
    render() {
      return (
        <View>
          <h1>foo</h1>
          <h2>bar</h2>
        </View>
      );
    }
Ocko
источник
2
это потому, что вы используете реагировать родной.
Ник H247
И фрагменты также доступны в React Native, так что на Viewсамом деле это не лучшее решение.
Эмиль Бержерон
0

Неверно: не только дочерние элементы

render(){
        return(
            <h2>Responsive Form</h2>
            <div>Adjacent JSX elements must be wrapped in an enclosing tag</div>
            <div className="col-sm-4 offset-sm-4">
                <form id="contact-form" onSubmit={this.handleSubmit.bind(this)} method="POST">
                    <div className="form-group">
                        <label for="name">Name</label>
                        <input type="text" className="form-control" id="name" />
                    </div>
                    <div className="form-group">
                        <label for="exampleInputEmail1">Email address</label>
                        <input type="email" className="form-control" id="email" aria-describedby="emailHelp" />
                    </div>
                    <div className="form-group">
                        <label for="message">Message</label>
                        <textarea className="form-control" rows="5" id="message"></textarea>
                    </div>
                    <button type="submit" className="btn btn-primary">Submit</button>
                </form>
            </div>
        )
    }

Действительный: корневой элемент под дочерними элементами

render(){
        return(
          <div>
            <h2>Responsive Form</h2>
            <div>Adjacent JSX elements must be wrapped in an enclosing tag</div>
            <div className="col-sm-4 offset-sm-4">
                <form id="contact-form" onSubmit={this.handleSubmit.bind(this)} method="POST">
                    <div className="form-group">
                        <label for="name">Name</label>
                        <input type="text" className="form-control" id="name" />
                    </div>
                    <div className="form-group">
                        <label for="exampleInputEmail1">Email address</label>
                        <input type="email" className="form-control" id="email" aria-describedby="emailHelp" />
                    </div>
                    <div className="form-group">
                        <label for="message">Message</label>
                        <textarea className="form-control" rows="5" id="message"></textarea>
                    </div>
                    <button type="submit" className="btn btn-primary">Submit</button>
                </form>
            </div>
          </div>
        )
    }
KARTHIKEYAN.A
источник
Пожалуйста , не «я тоже» ответы типа или повторять то же самое решение, если у вас есть что - то соответствующее для добавления к нему.
Эмиль Бержерон
-1

Для разработчиков Rect-Native. Я сталкиваюсь с этой ошибкой при рендеринге элемента в FlatList. У меня было два текстовых компонента. Я использовал их, как показано ниже

renderItem = { ({item}) => 
     <Text style = {styles.item}>{item.key}</Text>
     <Text style = {styles.item}>{item.user}</Text>
}

Но после того, как я положил эти буксирные компоненты Inside View, у меня все заработало

renderItem = { ({item}) => 
   <View style={styles.flatview}>
      <Text style = {styles.item}>{item.key}</Text>
      <Text style = {styles.item}>{item.user}</Text>
   </View>
 }

Возможно, вы используете другие компоненты, но их установка в View может быть вам полезна.

Гурджиндер Сингх
источник
Фрагменты также доступны в React Native, так что на Viewсамом деле это не лучшее решение.
Эмиль Бержерон
-1

Я думаю, что осложнение также может возникнуть при попытке вложить несколько Div в оператор return. Вы можете сделать это, чтобы ваши компоненты отображались как блочные элементы.

Вот пример правильного рендеринга нескольких компонентов с использованием нескольких элементов div.

return (
  <div>
    <h1>Data Information</H1>
    <div>
      <Button type="primary">Create Data</Button>
    </div>
  </div>
)
Sangha11
источник
-1

У меня была проблема, которая вызвала эту ошибку, которая не была очевидна.

const App = () => {
  return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
      </div>
  )
}

И здесь было исправление ...

const App = () => {
  return (
      <div className="AppClassName">       <--- note the change
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
      </div>
  )
}

Пойди разберись. Информация предоставлена ​​здесь на случай, если другие столкнутся с этим ударом. Очевидно, вы не можете иметь имя класса элемента, совпадающее с именем его родительской функции React.

zipzit
источник
Это не правда. Это даже по умолчанию в коде и шаблоне React. Что-то еще должно конфликтовать с этим именем класса.
Эмиль Бержерон