Erreur d'analyse: les éléments JSX adjacents doivent être enveloppés dans une balise englobante


467

J'essaie de configurer mon React.jsapplication pour qu'elle s'affiche uniquement si une variable que j'ai définie l'est true.

La façon dont ma fonction de rendu est configurée ressemble à ceci:

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

Fondamentalement, la partie importante ici est la if(this.state.submitted==false)partie (je veux que ces divéléments apparaissent lorsque la variable soumise est définie sur false).

Mais lors de l'exécution, j'obtiens l'erreur dans la question:

Erreur non interceptée: erreur d'analyse: ligne 38: les éléments JSX adjacents doivent être enveloppés dans une balise englobante

Quel est le problème ici? Et que puis-je utiliser pour que cela fonctionne?


3
stackoverflow.com/questions/25034994/… Les autres personnes ici vous disent simplement d'utiliser un élément parent, mais cela peut être inutile. Cette ancienne version de votre question a une réponse intéressante en utilisant des tableaux.
Meow

Réponses:


637

Vous devez placer votre composant entre une balise englobante, ce qui signifie:

// WRONG!

return (  
    <Comp1 />
    <Comp2 />
)

Au lieu:

// Correct

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

Edit: le commentaire de Per Joe Clay sur l' API Fragments

// More Correct

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

// Short syntax

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

4
Et si j'imprime 2 lignes ensemble dans un tableau. Comment puis-je envelopper <tr>?
Jose

235

Il est tard pour répondre à cette question mais je pensais que cela ajouterait à l'explication.

Cela se produit parce que n'importe où dans votre code, vous renvoyez deux éléments simultanément.

par exemple

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

Il doit être enveloppé dans un élément parent . par exemple

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


Explication plus détaillée

Votre jsxcode ci-dessous se transforme

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

dans ce

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

Mais si tu fais ça

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

cela est converti en ceci (juste à des fins d'illustration, en fait, vous obtiendrez 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'
        )
    }
  }]);

Dans le code ci-dessus, vous pouvez voir que vous essayez de retourner deux fois à partir d'un appel de méthode, ce qui est évidemment faux.

Edit- Dernières modifications dans React 16 et propres:

Si vous ne souhaitez pas ajouter de div supplémentaire à boucler et que vous souhaitez renvoyer plusieurs composants enfants, vous pouvez les utiliser React.Fragments.

React.Fragments sont un peu plus rapides et utilisent moins de mémoire (pas besoin de créer un nœud DOM supplémentaire, moins d'arbre DOM encombré).

par exemple (In 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

L'élément React ne doit renvoyer qu'un seul élément. Vous devrez envelopper vos deux balises avec une autre balise d'élément.

Je peux également voir que votre fonction de rendu ne renvoie rien. Voici à quoi devrait ressembler votre composant:

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

Notez également que vous ne pouvez pas utiliser d' ifinstructions à l'intérieur d'un élément renvoyé:

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

cela ne résout pas le problème avec le "si"; Si je supprime le "si" dans la fonction de rendu, cela fonctionne très bien.
user1072337

1
notez que vous ne pouvez pas utiliser si des déclarations à l'intérieur d'un élément recalibré. regardez ma réponse mise à jour
Matan Gubkin

99

Si vous ne voulez pas l'encapsuler dans une autre div comme l'ont suggéré d'autres réponses, vous pouvez également l'encapsuler dans un tableau et cela fonctionnera.

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

Il peut s'écrire:

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

Veuillez noter que ce qui précède générera un avertissement: Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of 'YourComponent'.

Cela peut être résolu en ajoutant un keyattribut aux composants, si vous les ajoutez manuellement, ajoutez-le comme:

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

Voici quelques informations supplémentaires sur les clés: Composition vs Héritage


7
J'ai essayé cela et cela me donne une erreur. Un élément React valide (ou nul) doit être retourné. Vous avez peut-être renvoyé undefined, un tableau ou un autre objet non valide.
prasadmsvs

3
@prasadmsvs +1 invariant.js: 39 Violation invariante non détectée: CommitFilter.render (): un ReactComponent valide doit être retourné. Vous avez peut-être renvoyé undefined, un tableau ou un autre objet non valide.
Svetlana Ivanova

1
Ceci est une excellente solution pour les moments où vous avez besoin / voulez éviter un élément wrapper!
bloudermilk

2
@aaaaaa ce n'est pas possible à cause du fonctionnement du réconciliateur React actuel. Il s'agit d'une pile et la réconciliation se fait récursivement. Dans React 16, cela est corrigé et vous pouvez maintenant retourner un tableau.
natnai

1
github.com/iamdustan/tiny-react-renderer est un excellent référentiel que chaque développeur React doit lire. Une fois que vous l'avez fait, vous devriez immédiatement comprendre pourquoi l'implémentation actuelle de react ne permet pas de renvoyer plusieurs enfants.
natnai

49

Le problème

Erreur d'analyse: les éléments JSX adjacents doivent être enveloppés dans une balise englobante

Cela signifie que vous essayez de renvoyer plusieurs éléments JSX frères de manière incorrecte. N'oubliez pas que vous n'écrivez pas du HTML, mais du JSX! Votre code est transposé de JSX en JavaScript. Par exemple:

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

sera transposé en:

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

Sauf si vous êtes nouveau dans la programmation en général, vous savez déjà que les fonctions / méthodes (de n'importe quel langage) prennent un certain nombre de paramètres mais ne renvoient toujours qu'une seule valeur. Cela étant, vous pouvez probablement constater qu'un problème survient lorsque vous essayez de renvoyer plusieurs composants frères en fonction de la façon dont createElement()fonctionne; il ne prend des paramètres que pour un élément et le renvoie. Par conséquent, nous ne pouvons pas renvoyer plusieurs éléments d'un même appel de fonction.


Donc, si vous vous êtes déjà demandé pourquoi cela fonctionne ...

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

mais pas ça ...

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

c'est parce que dans le premier extrait, les deux <p>éléments font partie childrende l' <div>élément. Lorsqu'ils en font partie, childrennous pouvons exprimer un nombre illimité d'éléments frères. Jetez un œil à la façon dont cela se transposerait:

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

Solutions

Selon la version de React que vous utilisez, vous disposez de quelques options pour résoudre ce problème:

  • Utilisez des fragments (React v16.2 + uniquement!)

    À partir de React v16.2, React prend en charge les fragments, qui est un composant sans nœud qui renvoie directement ses enfants.

    Le retour des enfants dans un tableau (voir ci-dessous) présente certains inconvénients:

    • Les enfants d'un tableau doivent être séparés par des virgules.
    • Les enfants d'un tableau doivent avoir une clé pour empêcher l'avertissement de clé de React.
    • Les chaînes doivent être entourées de guillemets.

    Ceux-ci sont éliminés de l'utilisation de fragments. Voici un exemple d'enfants enveloppés dans un fragment:

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

    qui désucre en:

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

    Notez que le premier extrait nécessite Babel v7.0 ou supérieur.


  • Retourne un tableau (React v16.0 + uniquement!)

    À partir de React v16, React Components peut renvoyer des tableaux. C'est différent des versions antérieures de React où vous étiez obligé d'envelopper tous les composants frères dans un composant parent.

    En d'autres termes, vous pouvez désormais:

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

    cela se traduit par:

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

    Notez que ce qui précède renvoie un tableau. Les tableaux sont des éléments React valides depuis React version 16 et ultérieure. Pour les versions antérieures de React, les tableaux ne sont pas des objets de retour valides!

    Notez également que ce qui suit n'est pas valide (vous devez renvoyer un tableau):

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

  • Envelopper les éléments dans un élément parent

    L'autre solution consiste à créer un composant parent qui encapsule les composants frères dans son children. C'est de loin le moyen le plus courant de résoudre ce problème et fonctionne dans toutes les versions de React.

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

    Remarque: Jetez un coup d'œil à nouveau en haut de cette réponse pour plus de détails et comment cela se transpile .


@Grievoushead, pas pour les composants, non. Uniquement pour les enfants.
Chris

1
Nice one, problème fixe pour moi.
Sohan

1
Merci Chris d'avoir pris le temps d'expliquer d'autres solutions!
Marvel Moe

sur React v16.4le premier exemple ne fonctionne pas en utilisant <><React.Fragment>
:,

@vsync, la prise en charge de cette syntaxe varie en fonction de l'environnement de votre bâtiment. Je ne sais pas si babel le supporte encore, et si oui, quelle version.
Chris

22

React 16.0.0 nous pouvons renvoyer plusieurs composants du rendu sous forme de tableau.

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

React 16.4.0 nous pouvons renvoyer plusieurs composants du rendu dans une balise Fragment. Fragment

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

Future React vous pourrez utiliser cette syntaxe abrégée. (de nombreux outils ne le prennent pas encore en charge, vous pouvez donc écrire de manière explicite <Fragment>jusqu'à ce que l'outillage rattrape.)

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

1
Vous avez oublié un ,entre les composants. C'est un tableau, vous devez donc séparer chaque élément qu'il contient.
Chris

Il n'y a pas <Fragment>, c'est <React.Fragment>. le dit dans votre propre lien
vsync

2
Si vous êtes en train de déstructurer, import React { Fragment } from 'react';vous l'utilisez comme ceci<Fragment >
Morris S

7

Si vous n'encapsulez pas votre composant, vous pouvez l'écrire comme indiqué ci-dessous.

Au lieu de:

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

vous pouvez écrire ceci:

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

6

c'est très simple, nous pouvons utiliser un élément parent div pour envelopper tous les éléments ou nous pouvons utiliser le concept de composant d'ordre supérieur (HOC), c'est-à-dire très utile pour réagir aux applications js

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

ou une autre meilleure façon est HOC c'est très simple pas très compliqué il suffit d'ajouter un fichier hoc.js dans votre projet et d'ajouter simplement ces codes

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

maintenant, importez le fichier hoc.js où vous voulez utiliser, maintenant au lieu d'envelopper avec l'élément div, nous pouvons envelopper avec hoc.

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

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

Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire concernant la manière et / ou la raison pour laquelle il résout le problème améliorerait la valeur à long terme de la réponse. Lisez ceci .
Shanteshwar Inde

La fonctionnalité dont vous parlez, c'est-à-dire appelée HOC dans reactjs. means props.children
Fazal Il y a

4

Il existe une règle selon laquelle une expression JSX doit avoir exactement un élément le plus à l'extérieur.

faux

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

correct

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

1

React 16 obtient votre retour sous forme de tableau, il doit donc être enveloppé par un élément comme div.

Mauvaise approche

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

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

    );
}

Approche à droite (tous les éléments d'une division ou d'un autre élément que vous utilisez)

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

Les composants React doivent être enveloppés dans un seul conteneur, qui peut être n'importe quelle balise, par exemple "<div> .. </ div>"

Vous pouvez vérifier la méthode de rendu de ReactCSSTransitionGroup


0

Importez la vue et encapsulez View. Envelopper dans un divne fonctionnait pas pour moi.

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

2
c'est parce que vous utilisez React Native.
Nick H247

Et des fragments sont également disponibles dans React Native, donc ce Viewn'est pas vraiment la meilleure solution.
Emile Bergeron

0

Non valide: pas seulement les éléments enfants

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

Valide: élément racine sous les éléments enfants

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

Veuillez éviter les réponses de type «moi aussi» ou répéter la même solution, sauf si vous avez quelque chose de pertinent à y ajouter .
Emile Bergeron

-1

Pour les développeurs Rect-Native. Je rencontre cette erreur lors du rendu d'Item dans FlatList. J'avais deux composants Text. Je les utilisais comme ci-dessous

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

Mais après avoir installé ces composants de vue intérieure, cela a fonctionné pour moi.

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

Vous utilisez peut-être d'autres composants, mais leur mise en vue peut être utile pour vous.


Les fragments sont également disponibles dans React Native, donc ce Viewn'est pas vraiment la meilleure solution.
Emile Bergeron

-1

Je pense que la complication peut également se produire lorsque vous essayez d'imbriquer plusieurs Divs dans l'instruction return. Vous pouvez le faire pour vous assurer que vos composants s'affichent en tant qu'éléments de bloc.

Voici un exemple de rendu correct de deux composants, en utilisant plusieurs divs.

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

-1

J'ai eu un problème qui a généré cette erreur qui n'était pas évidente tous ensemble.

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

Et voici la solution ...

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

Allez comprendre. Informations partagées ici au cas où d'autres frapperaient cette bosse. Apparemment, vous ne pouvez pas avoir un nom de classe d'élément identique à son nom de fonction parent React.


Ce n'est pas vrai. C'est même la valeur par défaut dans le modèle codesandbox React. Quelque chose d'autre a dû entrer en conflit avec ce nom de classe.
Emile Bergeron
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.