Error de análisis: los elementos JSX adyacentes deben estar envueltos en una etiqueta adjunta

467

Estoy tratando de configurar mi React.jsaplicación para que solo se muestre si una variable que configuré es true.

La forma en que está configurada mi función de renderización es la siguiente:

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

Básicamente, la parte importante aquí es la if(this.state.submitted==false)parte (quiero que estos divelementos se muestren cuando la variable presentada esté configurada en false).

Pero cuando ejecuto esto, aparece el error en la pregunta:

Error no detectado: Error de análisis: Línea 38: Los elementos JSX adyacentes deben estar envueltos en una etiqueta adjunta

Cuál es el problema aquí? ¿Y qué puedo usar para que esto funcione?

usuario1072337
fuente
3
stackoverflow.com/questions/25034994/… Las otras personas aquí solo le están diciendo que use un elemento padre, pero eso puede ser innecesario. Esta versión anterior de su pregunta tiene una respuesta interesante usando matrices.
Miau

Respuestas:

637

Debe colocar su componente entre una etiqueta adjunta, lo que significa:

// WRONG!

return (  
    <Comp1 />
    <Comp2 />
)

En lugar:

// Correct

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

Editar: según el comentario de Joe Clay sobre la API Fragments

// More Correct

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

// Short syntax

return (
    <>
       <Comp1 />
       <Comp2 />
    </>
)
wdanxna
fuente
44
¿Qué pasa si estoy imprimiendo 2 filas juntas en una tabla? ¿Cómo puedo envolver <tr>?
José
235

Es tarde para responder esta pregunta, pero pensé que se agregaría a la explicación.

Está sucediendo porque en cualquier parte de su código está devolviendo dos elementos simultáneamente.

p.ej

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

Debe estar envuelto en un elemento padre . p.ej

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


Explicación más detallada

Su jsxcódigo a continuación se transforma

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

dentro de esto

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

Pero si haces esto

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

esto se convierte en esto (solo con fines ilustrativos, en realidad lo obtendrás 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'
        )
    }
  }]);

En el código anterior, puede ver que está intentando regresar dos veces desde una llamada de método, lo que obviamente es incorrecto.

Editar: últimos cambios en React 16 y en los propios barrios:

Si no desea agregar div extra para envolver y desea devolver más de un componente secundario, puede utilizarlo React.Fragments.

React.Fragments son un poco más rápidos y tienen menos uso de memoria (no es necesario crear un nodo DOM adicional, menos árbol DOM desordenado).

por ejemplo (en React 16.2.0)

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

o

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

o

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

Reaccionar elemento tiene que devolver solo un elemento. Tendrás que envolver ambas etiquetas con otra etiqueta de elemento.

También puedo ver que su función de renderizado no devuelve nada. Así es como debería verse su componente:

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

También tenga en cuenta que no puede usar ifdeclaraciones dentro de un elemento devuelto:

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 />
    }
},
Matan Gubkin
fuente
esto no soluciona el problema con el "si"; Si elimino el "si" dentro de la función de renderizado, funciona bien.
user1072337
1
tenga en cuenta que no puede usar si las declaraciones dentro de un elemento sintonizado. mira mi respuesta actualizada
Matan Gubkin
99

Si no desea envolverlo en otro div como han sugerido otras respuestas, también puede envolverlo en una matriz y funcionará.

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

Se puede escribir como:

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

Tenga en cuenta que lo anterior generará una advertencia: Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of 'YourComponent'.

Esto se puede solucionar agregando un keyatributo a los componentes, si agrega estos manualmente, agréguelo como:

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

Aquí hay más información sobre las claves: Composición vs Herencia

Neal
fuente
77
Intenté esto y me da un error. Se debe devolver un elemento React válido (o nulo). Es posible que haya devuelto indefinido, una matriz u otro objeto no válido.
prasadmsvs
3
@prasadmsvs +1 invariant.js: 39 Infracción invariable no capturada: CommitFilter.render (): se debe devolver un ReactComponent válido. Es posible que haya devuelto indefinido, una matriz u otro objeto no válido.
Svetlana Ivanova
1
¡Esta es una gran solución para momentos en los que necesita / desea evitar un elemento envoltorio!
leche blouder
2
@aaaaaa no es posible debido a la forma en que funciona el reconciliador React actual. Es una pila y la reconciliación se realiza de forma recursiva. En React 16 esto está arreglado, y ahora puede devolver una matriz.
natnai
1
github.com/iamdustan/tiny-react-renderer es un excelente repositorio que todo desarrollador de reacciones debería leer. Una vez que lo haga, debería ser inmediatamente evidente para usted por qué la implementación actual de react no permite devolver múltiples hijos.
natnai
49

El problema

Error de análisis: los elementos JSX adyacentes deben estar envueltos en una etiqueta adjunta

Esto significa que está intentando devolver varios elementos JSX hermanos de manera incorrecta. ¡Recuerde que no está escribiendo HTML, sino JSX! Su código se transpila de JSX a JavaScript. Por ejemplo:

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

será trasladado a:

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

A menos que sea nuevo en la programación en general, ya sabe que las funciones / métodos (de cualquier lenguaje) toman cualquier número de parámetros pero siempre solo devuelven un valor. Dado eso, probablemente pueda ver que surge un problema al intentar devolver múltiples componentes hermanos en función de cómo createElement()funciona; solo toma parámetros para un elemento y lo devuelve. Por lo tanto, no podemos devolver múltiples elementos de una llamada de función.


Entonces, si alguna vez te has preguntado por qué esto funciona ...

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

pero no esto ...

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

es porque en el primer fragmento, ambos <p>-elementos son parte de childrenla <div>-elemento. Cuando son parte de childrenentonces, podemos expresar un número ilimitado de elementos hermanos. Eche un vistazo a cómo se transpilaría esto:

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

Soluciones

Dependiendo de la versión de React que esté ejecutando, tiene algunas opciones para abordar esto:

  • Usar fragmentos (¡React v16.2 + solo!)

    A partir de React v16.2, React tiene soporte para Fragments, que es un componente sin nodo que devuelve sus hijos directamente.

    Devolver a los hijos en una matriz (ver más abajo) tiene algunos inconvenientes:

    • Los niños en una matriz deben estar separados por comas.
    • Los niños en una matriz deben tener una clave para evitar la advertencia de clave de React.
    • Las cadenas deben estar entre comillas.

    Estos se eliminan del uso de fragmentos. Aquí hay un ejemplo de niños envueltos en un fragmento:

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

    que quita el azúcar en:

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

    Tenga en cuenta que el primer fragmento requiere Babel v7.0 o superior.


  • Devuelve una matriz (¡solo React v16.0 +!)

    A partir de React v16, React Components puede devolver matrices. Esto es diferente a las versiones anteriores de React donde se vio obligado a envolver todos los componentes hermanos en un componente principal.

    En otras palabras, ahora puedes hacer:

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

    esto se traduce en:

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

    Tenga en cuenta que lo anterior devuelve una matriz. Las matrices son elementos de reacción válidos desde React versión 16 y posteriores. ¡Para versiones anteriores de React, las matrices no son objetos de retorno válidos!

    También tenga en cuenta que lo siguiente no es válido (debe devolver una matriz):

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

  • Envuelva los elementos en un elemento padre

    La otra solución implica crear un componente padre que envuelva los componentes hermanos en su children. Esta es, con mucho, la forma más común de abordar este problema, y ​​funciona en todas las versiones de React.

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

    Nota: Eche un vistazo nuevamente a la parte superior de esta respuesta para obtener más detalles y cómo se transmite .

Chris
fuente
@Grievoushead, no para componentes, no. Solo para niños.
Chris
1
Buena, problema resuelto para mí.
Sohan
1
¡Gracias Chris por tomarse el tiempo para explicar otras soluciones!
Marvel Moe
en Reaccionar v16.4el primer ejemplo no funciona usando:, <>solo <React.Fragment>:(
vsync
@vsync el soporte para esa sintaxis varía en su entorno de construcción. No estoy seguro si Babel lo admite todavía, y si es así, qué versión.
Chris
22

React 16.0.0 podemos devolver múltiples componentes de render como una matriz.

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

React 16.4.0 podemos devolver múltiples componentes desde render en una etiqueta Fragment. Fragmento

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

Future React podrá utilizar esta sintaxis abreviada. (muchas herramientas aún no lo admiten, por lo que es posible que desee escribir explícitamente <Fragment>hasta que las herramientas se pongan al día).

return (
<>
    <Comp1 />
    <Comp2 />
</>)
Morris S
fuente
1
Olvidaste un ,entre los componentes. Es una matriz, por lo que debe separar cada elemento dentro de ella.
Chris
No hay <Fragment>, es <React.Fragment>. lo dice en su propio enlace
vsync
2
Si está desestructuración import React { Fragment } from 'react';entonces utilizar de esta manera<Fragment >
Morris S
7

Si no ajusta su componente, puede escribirlo como se menciona a continuación.

En vez de:

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

puedes escribir esto:

return[(
 <Comp1 />
),
(
<Comp2 />
) ];
ronak ganatra
fuente
6

es muy simple, podemos usar un elemento padre div para envolver todo el elemento o podemos usar el concepto de Componente de orden superior (HOC), es decir, muy útil para aplicaciones de reacción js

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

u otra mejor manera es HOC, es muy simple, no muy complicado, solo agregue un archivo hoc.js en su proyecto y simplemente agregue estos códigos

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

ahora importa el archivo hoc.js donde quieras usar, ahora en lugar de envolver con el elemento div podemos envolver con hoc.

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

    render() {
      return (
    <Hoc>
        <div>foo</div>
        <div>bar</div>
    </Hoc>
      );
    }
Fazal
fuente
Si bien este código puede responder la pregunta, proporcionar un contexto adicional con respecto a cómo y / o por qué resuelve el problema mejoraría el valor a largo plazo de la respuesta. Lea este .
Shanteshwar Inde
La funcionalidad de la que está hablando, es decir, llamada HOC en reactjs. significa props.children
Fazal
4

Hay una regla en reaccionar que una expresión JSX debe tener exactamente un elemento más externo.

incorrecto

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

correcto

const para = (
    <div>
        <p></p>
        <p></p>
    </div>
);
Khandelwal-manik
fuente
1

React 16 obtiene su retorno como una matriz, por lo que debe estar envuelto por un elemento como div.

Enfoque equivocado

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

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

    );
}

Enfoque correcto (Todos los elementos en un div u otro elemento que está utilizando)

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

            <button className="btn btn-primary" onClick={()=>this.addTodo(this.state.value)}>Submit</button>
        </div>
    );
}
Abdul Moiz
fuente
1

Los componentes de reacción deben envolverse en un solo contenedor, que puede ser cualquier etiqueta, por ejemplo, "<div> .. </ div>"

Puede consultar el método de representación de ReactCSSTransitionGroup

Shivprasad P
fuente
0

Importar vista y envolver View. Envolver en un divno funcionó para mí.

import { View } from 'react-native';
...
    render() {
      return (
        <View>
          <h1>foo</h1>
          <h2>bar</h2>
        </View>
      );
    }
Ocko
fuente
2
eso es porque estás usando reaccionar nativo.
Nick H247
Y los fragmentos también están disponibles en React Native, por lo que a Viewno es realmente la mejor solución.
Emile Bergeron
0

Inválido: no solo elementos secundarios

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 raíz debajo de elementos secundarios

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
fuente
Evite las respuestas de tipo "yo también" o repita la misma solución, a menos que tenga algo relevante que agregar .
Emile Bergeron
-1

Para desarrolladores Rect-Native. Me encuentro con este error al renderizar el elemento en FlatList. Tenía dos componentes de texto. Los estaba usando como a continuación

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

Pero después de poner estos componentes Tow View View internos, funcionó para mí.

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

Es posible que esté utilizando otros componentes, pero ponerlos en Vista puede funcionar para usted.

Gurjinder Singh
fuente
Los fragmentos también están disponibles en React Native, por lo que a Viewno es realmente la mejor solución.
Emile Bergeron
-1

Creo que la complicación también puede ocurrir al intentar anidar múltiples Divs dentro de la declaración de devolución. Es posible que desee hacer esto para asegurarse de que sus componentes se procesen como elementos de bloque.

Aquí hay un ejemplo de cómo representar correctamente un par de componentes, utilizando múltiples divs.

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

Tuve un problema que generó este error que no era del todo obvio.

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

Y aquí estaba la solución ...

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

Imagínate. Información compartida aquí en caso de que otros golpeen este obstáculo Aparentemente, no puede tener un nombre de clase de elemento igual al nombre de la función primaria React.

zipzit
fuente
Eso no es cierto. Incluso es el valor predeterminado en la plantilla React de codesandbox. Algo más debe haber entrado en conflicto con este nombre de clase.
Emile Bergeron