Erro de análise: Os elementos JSX adjacentes devem ser agrupados em uma tag anexa


467

Estou tentando configurar meu React.js aplicativo para que ele seja renderizado apenas se uma variável que eu definir for true.

A maneira como minha função de renderização é configurada se parece com:

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

Basicamente, a parte importante aqui é a if(this.state.submitted==false)parte (eu quero essasdiv elementos apareçam quando a variável enviada estiver configurada como false).

Mas ao executar isso, recebo o erro na pergunta:

Erro não capturado: Erro de análise: Linha 38: Os elementos JSX adjacentes devem ser agrupados em uma tag anexa

Qual é o problema aqui? E o que posso usar para fazer isso funcionar?


3
stackoverflow.com/questions/25034994/… As outras pessoas aqui estão apenas dizendo para você usar um elemento pai, mas isso pode ser desnecessário. Esta versão mais antiga da sua pergunta tem uma resposta interessante usando matrizes.
Miau

Respostas:


637

Você deve colocar seu componente entre uma tag anexa, o que significa:

// WRONG!

return (  
    <Comp1 />
    <Comp2 />
)

Em vez de:

// Correct

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

Edit: Por comentário de Joe Clay sobre a API Fragments

// More Correct

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

// Short syntax

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

4
E se eu estiver imprimindo 2 linhas juntas em uma tabela. Como posso quebrar <tr>?
Jose

235

É tarde para responder a essa pergunta, mas pensei que isso aumentaria a explicação.

Isso está acontecendo porque em qualquer lugar do seu código você está retornando dois elementos simultaneamente.

por exemplo

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

Deve ser envolto em um elemento pai . por exemplo

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


Explicação mais detalhada

Seu jsxcódigo abaixo é transformado

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

nisso

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

Mas se você fizer isso

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

isso é convertido nisso (apenas para fins ilustrativos, você receberá 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'
        )
    }
  }]);

No código acima, você pode ver que está tentando retornar duas vezes de uma chamada de método, o que está obviamente errado.

Editar- Últimas alterações no React 16 e nas próprias enfermarias:

Se você não deseja adicionar div extra para contornar e deseja devolver mais de um componente filho, pode usar React.Fragments.

React.Fragments são um pouco mais rápidos e têm menos uso de memória (não é necessário criar um nó DOM extra, uma árvore DOM menos confusa).

por exemplo (no React 16.2.0)

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

ou

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

ou

render() {
 return [
  "Some text.",
  <h2 key="heading-1">A heading</h2>,
  "More text.",
  <h2 key="heading-2">Another heading</h2>,
  "Even more text."
 ];
}

115

O elemento reagir deve retornar apenas um elemento. Você precisará agrupar as duas tags com outra tag de elemento.

Também posso ver que sua função de renderização não está retornando nada. É assim que seu componente deve ficar:

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

Observe também que você não pode usar ifinstruções dentro de um elemento retornado:

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

isso não resolve o problema com o "se"; Se eu remover o "se" dentro da função de renderização, ele funcionará bem.
precisa saber é o seguinte

1
observe que você não pode usar se as declarações estiverem dentro de um elemento reajustado. veja a minha resposta atualizada
Matan Gubkin

99

Se você não deseja agrupá-lo em outra div, como sugeriram outras respostas, também pode agrupá-lo em uma matriz e ele funcionará.

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

Pode ser escrito como:

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

Observe que o acima irá gerar um aviso: Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of 'YourComponent'.

Isso pode ser corrigido adicionando um keyatributo aos componentes, se adicionar manualmente, adicione-o como:

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

Aqui estão mais algumas informações sobre as chaves: Composição x Herança


7
Eu tentei isso e isso me dá um erro. Um elemento React válido (ou nulo) deve ser retornado. Você pode ter retornado indefinido, uma matriz ou outro objeto inválido.
Prasadmsvs 26/05

3
@prasadmsvs +1 invariant.js: 39 Violação invariável não capturada: CommitFilter.render (): Um ReactComponent válido deve ser retornado. Você pode ter retornado indefinido, uma matriz ou outro objeto inválido.
Svetlana Ivanova

1
Esta é uma ótima solução para momentos em que você precisa / deseja evitar um elemento de invólucro!
bloudermilk

2
@aaaaaa não é possível devido à maneira como o atual React reconciler funciona. É uma pilha e a reconciliação é feita recursivamente. No React 16, isso é corrigido e agora você pode retornar uma matriz.
Natnai #

1
O github.com/iamdustan/tiny-react-renderer é um excelente repositório que todo desenvolvedor de reação deve ler. Depois de fazer isso, deve ficar imediatamente claro para você por que a implementação atual do react não permite o retorno de vários filhos.
Natnai #

49

O problema

Erro de análise: Os elementos JSX adjacentes devem ser agrupados em uma tag anexa

Isso significa que você está tentando retornar vários elementos JSX irmãos de maneira incorreta. Lembre-se de que você não está escrevendo HTML, mas JSX! Seu código é transpilado do JSX para o JavaScript. Por exemplo:

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

será transpilado para:

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

A menos que você seja iniciante em programação em geral, você já sabe que funções / métodos (de qualquer idioma) usam qualquer número de parâmetros, mas sempre retornam apenas um valor. Dado isso, você provavelmente pode ver que surge um problema ao tentar retornar vários componentes irmãos com base em como createElement()funciona; leva apenas parâmetros para um elemento e retorna isso. Portanto, não podemos retornar vários elementos de uma chamada de função.


Então, se você já se perguntou por que isso funciona ...

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

mas não isso ...

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

é porque no primeiro trecho, ambos <p>-elements fazem parte childrendo <div>-element. Quando eles fazem parte children, podemos expressar um número ilimitado de elementos irmãos. Veja como isso se traduziria:

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

Soluções

Dependendo da versão do React que você está executando, você tem algumas opções para resolver isso:

  • Use fragmentos (somente no React v16.2 +!)

    A partir do React v16.2, o React tem suporte para Fragments, que é um componente sem nós que retorna seus filhos diretamente.

    O retorno dos filhos em uma matriz (veja abaixo) tem algumas desvantagens:

    • Filhos em uma matriz devem ser separados por vírgulas.
    • Filhos em uma matriz devem ter uma chave para impedir o aviso de chave do React.
    • As strings devem estar entre aspas.

    Estes são eliminados do uso de fragmentos. Aqui está um exemplo de crianças envolvidas em um fragmento:

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

    que desengordura em:

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

    Observe que o primeiro trecho requer o Babel v7.0 ou superior.


  • Retornar uma matriz (somente React v16.0 +!)

    A partir do React v16, o React Components pode retornar matrizes. Isso é diferente das versões anteriores do React, nas quais você era forçado a agrupar todos os componentes irmãos em um componente pai.

    Em outras palavras, agora você pode fazer:

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

    isso transpila para:

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

    Observe que o acima mencionado retorna uma matriz. As matrizes são elementos do React válidos desde o React versão 16 e posterior. Para versões anteriores do React, matrizes não são objetos de retorno válidos!

    Observe também que o seguinte é inválido (você deve retornar uma matriz):

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

  • Quebrar os elementos em um elemento pai

    A outra solução envolve a criação de um componente pai que envolve os componentes irmãos children. Essa é de longe a maneira mais comum de resolver esse problema e funciona em todas as versões do React.

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

    Nota: Dê uma olhada novamente na parte superior desta resposta para obter mais detalhes e como isso se traduz .


@ Grievoushead, não para componentes, não. Somente para crianças.
Chris

1
Agradável, problema corrigido para mim.
Sohan 26/09

1
Obrigado Chris por reservar um tempo para explicar outras soluções!
Marvel Moe

em React, v16.4o primeiro exemplo não funciona usando <><React.Fragment>
:,

@vsync, o suporte para essa sintaxe varia de acordo com o ambiente de construção. Não tenho certeza se o babel ainda o suporta e, em caso afirmativo, qual versão.
22418 Chris

22

Na reação 16.0.0 , podemos retornar vários componentes da renderização como uma matriz.

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

Na reação 16.4.0 , podemos retornar vários componentes da renderização em uma tag Fragment. Fragmento

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

Reação futura, você poderá usar esta sintaxe abreviada. (muitas ferramentas ainda não a suportam, portanto, você pode escrever explicitamente <Fragment>até que as ferramentas sejam atualizadas.)

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

1
Você esqueceu um ,entre os componentes. É uma matriz, então você precisa separar cada elemento dentro dela.
27417 Chris

Não há <Fragment>, é <React.Fragment>. diz isso em seu próprio link
vsync

2
Se você está desestruturando import React { Fragment } from 'react';então você usá-lo como este<Fragment >
Morris S

7

Se você não quebrar o seu componente, poderá escrevê-lo como mencionado abaixo no método.

Ao invés de:

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

você pode escrever isto:

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

6

é muito simples, podemos usar um elemento pai div para agrupar todo o elemento ou podemos usar o conceito de componente de ordem superior (HOC), ou seja, muito útil para aplicações js react

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

ou outra melhor maneira é HOC, é muito simples, não muito complicado, basta adicionar um arquivo hoc.js ao seu projeto e simplesmente adicionar esses códigos

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

agora importe o arquivo hoc.js para o qual deseja usar; agora, em vez de agrupar com o elemento div, podemos agrupar com hoc.

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

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

Embora esse código possa responder à pergunta, fornecer um contexto adicional sobre como e / ou por que resolve o problema melhoraria o valor a longo prazo da resposta. Leia isto .
Shanteshwar Inde

A funcionalidade que você está falando, ou seja, chamada HOC no reactjs. significa props.children
Fazal

4

Há uma regra no react que uma expressão JSX deve ter exatamente um elemento mais externo.

errado

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

corrigir

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

1

O React 16 obtém seu retorno como uma matriz, portanto deve ser envolvido por um elemento como div.

Abordagem errada

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

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

    );
}

Abordagem correta (todos os elementos em uma div ou outro elemento que você está usando)

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

Os componentes de reação devem ser empacotados em um único contêiner, que pode ser qualquer tag, por exemplo, "<div> .. </ div>"

Você pode verificar o método de renderização de ReactCSSTransitionGroup


0

Importe a visualização e envolva View. Envolvendo um divnão funcionou para mim.

import { View } from 'react-native';
...
    render() {
      return (
        <View>
          <h1>foo</h1>
          <h2>bar</h2>
        </View>
      );
    }

2
isso é porque você está usando o reagir nativo.
Nick H247 #

E os fragmentos também estão disponíveis no React Native, portanto, essa Viewnão é realmente a melhor solução.
Emile Bergeron

0

Inválido: não apenas elementos filho

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

Válido: elemento raiz em elementos filho

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

Evite "eu também" digite respostas ou repita a mesma solução, a menos que você tenha algo relevante a acrescentar .
Emile Bergeron

-1

Para desenvolvedores Rect-Native. Eu encontrei esse erro ao renderizarItem no FlatList. Eu tinha dois componentes de texto. Eu estava usando-os como abaixo

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

Mas depois que eu coloquei esses componentes no Inside View, funcionou para mim.

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

Você pode estar usando outros componentes, mas colocá-los no modo de exibição pode ser útil para você.


Fragmentos também estão disponíveis no React Native, portanto, uma Viewnão é realmente a melhor solução.
Emile Bergeron

-1

Acho que a complicação também pode ocorrer ao tentar aninhar vários Divs na declaração de retorno. Você pode fazer isso para garantir que seus componentes sejam renderizados como elementos de bloco.

Aqui está um exemplo de renderização correta de alguns componentes, usando várias divs.

return (
  <div>
    <h1>Data Information</H1>
    <div>
      <Button type="primary">Create Data</Button>
    </div>
  </div>
)

-1

Eu tive um problema que gerou esse erro que nem todos eram óbvios.

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

E aqui estava a solução ...

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

Vai saber. Informações compartilhadas aqui caso outras pessoas cheguem a esse problema. Aparentemente, você não pode ter um nome de classe de elemento igual ao nome da função pai de React.


Isso não é verdade. É até o padrão no modelo codesandbox React. Outra coisa deve ter entrado em conflito com esse nome de classe.
Emile Bergeron
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.