Błąd analizy: sąsiednie elementy JSX muszą być owinięte w otaczający tag


467

Próbuję skonfigurować moją React.jsaplikację, aby renderowała się tylko wtedy, gdy ustawiona jest zmienna true.

Sposób konfiguracji mojej funkcji renderowania wygląda następująco:

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

Zasadniczo ważną częścią jest tutaj if(this.state.submitted==false)część (chcę, aby te divelementy pojawiały się, gdy ustawiona zmienna jest ustawiona na false).

Ale podczas uruchamiania pojawia się błąd w pytaniu:

Nieprzechwycony błąd: Błąd analizy: wiersz 38: Sąsiadujące elementy JSX muszą być owinięte w otaczający tag

O co tu chodzi? I czego mogę użyć, aby to zadziałało?


3
stackoverflow.com/questions/25034994/ ... Pozostałe osoby mówią tylko, abyś użył elementu nadrzędnego, ale może to być niepotrzebne. Ta starsza wersja twojego pytania ma interesującą odpowiedź za pomocą tablic.
Miau

Odpowiedzi:


636

Powinieneś umieścić swój komponent między zamykającym tagiem, co oznacza:

// WRONG!

return (  
    <Comp1 />
    <Comp2 />
)

Zamiast:

// Correct

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

Edycja: komentarz Per Joe Claya na temat API Fragments

// More Correct

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

// Short syntax

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

4
Co jeśli wydrukuję 2 wiersze razem w tabeli. Jak mogę owinąć <tr>?
Jose

235

Późno jest odpowiedzieć na to pytanie, ale pomyślałem, że doda to wyjaśnienia.

Dzieje się tak, ponieważ w dowolnym miejscu w kodzie zwracane są jednocześnie dwa elementy.

na przykład

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

Powinien być zawinięty w element nadrzędny . na przykład

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


Bardziej szczegółowe wyjaśnienie

Twój poniższy jsxkod zostanie przekształcony

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

zaangażowany w to

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

Ale jeśli to zrobisz

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

przekształca się w to (tylko dla celów ilustracyjnych, faktycznie dostaniesz 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'
        )
    }
  }]);

W powyższym kodzie widać, że próbujesz powrócić dwukrotnie z wywołania metody, co jest oczywiście błędne.

Edycja - Ostatnie zmiany w React 16 i totemy własne:

Jeśli nie chcesz dodawać dodatkowych elementów div, aby je zawinąć i chcesz zwrócić więcej niż jeden element podrzędny, z którym możesz skorzystać React.Fragments.

React.Fragments są nieco szybsze i mają mniejsze zużycie pamięci (nie trzeba tworzyć dodatkowego węzła DOM, mniej zaśmiecone drzewo DOM).

np. (w React 16.2.0)

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

lub

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

lub

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

115

Element React musi zwrócić tylko jeden element. Będziesz musiał zawinąć oba swoje tagi innym tagiem elementu.

Widzę też, że twoja funkcja renderowania nic nie zwraca. Tak powinien wyglądać twój komponent:

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

Należy również pamiętać, że nie można używać ifinstrukcji wewnątrz zwracanego elementu:

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

nie rozwiązuje to problemu z „if”; Jeśli usunę „if” w funkcji renderowania, będzie działać dobrze.
user1072337

1
zwróć uwagę, że nie możesz użyć, jeśli statystyki wewnątrz zestrojonego elementu. spójrz na moją zaktualizowaną odpowiedź
Matan Gubkin

99

Jeśli nie chcesz zawijać go w inny div, jak sugerują inne odpowiedzi, możesz również zawinąć go w tablicę i będzie działać.

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

Można go zapisać jako:

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

Pamiętaj, że powyższe wygeneruje ostrzeżenie: Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of 'YourComponent'.

Można to naprawić, dodając keyatrybut do komponentów, jeśli dodając je ręcznie, dodaj go w następujący sposób:

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

Oto kilka dodatkowych informacji na temat kluczy: Kompozycja vs. Dziedziczenie


7
Próbowałem tego i daje mi to błąd. Prawidłowy element React (lub null) musi zostać zwrócony. Możliwe, że zwróciłeś niezdefiniowany obiekt, tablicę lub inny nieprawidłowy obiekt.
prasadmsvs

3
@prasadmsvs +1 invariant.js: 39 Uncaught Invariant Violation: CommitFilter.render (): Należy zwrócić prawidłowy ReactComponent. Możliwe, że zwróciłeś niezdefiniowany obiekt, tablicę lub inny nieprawidłowy obiekt.
Svetlana Ivanova

1
To świetne rozwiązanie na czas, gdy potrzebujesz / chcesz uniknąć elementu opakowania!
bloudermilk

2
@ aaaaaa nie jest możliwe ze względu na sposób działania obecnego narzędzia do uzgadniania React. Jest to stos, a uzgadnianie odbywa się rekurencyjnie. W React 16 jest to naprawione i możesz teraz zwrócić tablicę.
natnai

1
github.com/iamdustan/tiny-react-renderer to doskonałe repozytorium, które każdy programista powinien przeczytać. Gdy to zrobisz, powinno być od razu widoczne, dlaczego obecna implementacja metody reagowania nie pozwala na powrót wielu dzieci.
natnai

49

Problem

Błąd analizy: sąsiednie elementy JSX muszą być owinięte w otaczający tag

Oznacza to, że próbujesz zwrócić wiele elementów JSX rodzeństwa w niepoprawny sposób. Pamiętaj, że nie piszesz HTML, ale JSX! Twój kod jest transpilowany z JSX do JavaScript. Na przykład:

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

zostaną przełożone na:

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

Jeśli nie jesteś początkujący w programowaniu, wiesz już, że funkcje / metody (dowolnego języka) przyjmują dowolną liczbę parametrów, ale zawsze zwracają tylko jedną wartość. Biorąc to pod uwagę, prawdopodobnie możesz zauważyć, że pojawia się problem podczas próby zwrócenia wielu komponentów rodzeństwa w zależności od tego, jak createElement()działa; pobiera parametry tylko dla jednego elementu i zwraca to. Dlatego nie możemy zwrócić wielu elementów z jednego wywołania funkcji.


Więc jeśli kiedykolwiek zastanawiałeś się, dlaczego to działa ...

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

ale nie to ...

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

to dlatego, że w pierwszym fragmencie, obie <p>-elements są częścią childrentego <div>-elementowe. Gdy są one częścią children, możemy wyrazić nieograniczoną liczbę elementów rodzeństwa. Zobacz, jak to się przełoży:

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

Rozwiązania

W zależności od używanej wersji programu React masz kilka opcji rozwiązania tego problemu:

  • Używaj fragmentów (tylko React v16.2 +!)

    Począwszy od wersji 16.2 React, React obsługuje Fragmenty, które są komponentem bez węzłów, który zwraca bezpośrednio swoje elementy potomne.

    Zwracanie dzieci do tablicy (patrz poniżej) ma pewne wady:

    • Dzieci w tablicy muszą być oddzielone przecinkami.
    • Dzieci w tablicy muszą mieć klucz, aby zapobiec ostrzeżeniu o kluczu React.
    • Ciągi muszą być owinięte w cudzysłów.

    Są one eliminowane ze stosowania fragmentów. Oto przykład dzieci owiniętych we fragment:

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

    który de-cukry na:

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

    Pamiętaj, że pierwszy fragment wymaga Babel w wersji 7.0 lub nowszej.


  • Zwraca tablicę (React v16.0 +!)

    Począwszy od wersji 16 React, komponenty React mogą zwracać tablice. Jest to inaczej niż we wcześniejszych wersjach React, w których zmuszono Cię do zawinięcia wszystkich komponentów rodzeństwa w komponent nadrzędny.

    Innymi słowy, możesz teraz:

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

    przekłada się to na:

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

    Zauważ, że powyższe zwraca tablicę. Tablice są poprawnymi elementami React od wersji React 16 i późniejszych. We wcześniejszych wersjach React tablice nie są prawidłowymi obiektami zwrotnymi!

    Pamiętaj również, że następujące informacje są nieprawidłowe (musisz zwrócić tablicę):

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

  • Zawiń elementy w element nadrzędny

    Drugie rozwiązanie polega na utworzeniu komponentu nadrzędnego, który zawija w nim komponenty rodzeństwa children. Jest to zdecydowanie najczęstszy sposób rozwiązania tego problemu i działa we wszystkich wersjach React.

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

    Uwaga: spójrz ponownie na górę tej odpowiedzi, aby uzyskać więcej informacji i sposób jej transpozycji .


@Grievoushead, nie dla komponentów, nie. Tylko dla dzieci.
Chris,

1
Niezły, naprawiono mi problem.
Sohan,

1
Dzięki Chris za poświęcenie czasu na wyjaśnienie innych rozwiązań!
Marvel Moe

na React v16.4pierwszy przykład nie działa przy użyciu <><React.Fragment>
:,

@vsync obsługa tej składni różni się w zależności od środowiska budynku. Nie jestem pewien, czy Babel go obsługuje, a jeśli tak, to która wersja.
Chris

22

React 16.0.0 możemy zwrócić wiele komponentów z renderowania jako tablicę.

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

Reaguj 16.4.0 możemy zwrócić wiele komponentów z renderowania w tagu Fragment. Fragment

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

Future React będziesz mógł używać tej skróconej składni. (wiele narzędzi jeszcze go nie obsługuje, więc możesz chcieć pisać, <Fragment>dopóki narzędzie nie nadejdzie)

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

1
Zapomniałeś ,między komponentami. Jest to tablica, więc musisz oddzielić każdy element w sobie.
Chris

Nie ma to <Fragment>, to <React.Fragment>. tak mówi we własnym linku
vsync

2
Jeśli import React { Fragment } from 'react';<Fragment >
Morris S

7

Jeśli nie zawiniesz swojego komponentu, możesz napisać go w sposób opisany poniżej.

Zamiast:

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

możesz to napisać:

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

6

to bardzo proste, możemy użyć elementu div elementu nadrzędnego, aby owinąć cały element, lub możemy zastosować koncepcję komponentu wyższego rzędu (HOC), tj. bardzo przydatne do reagowania na aplikacje js

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

lub innym najlepszym sposobem jest HOC jest bardzo prosty, niezbyt skomplikowany, po prostu dodaj plik hoc.js do swojego projektu i po prostu dodaj te kody

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

teraz zaimportuj plik hoc.js tam, gdzie chcesz użyć, teraz zamiast owijania elementem div możemy owinąć go hoc.

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

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

Chociaż ten kod może odpowiedzieć na pytanie, zapewnienie dodatkowego kontekstu dotyczącego tego, jak i / lub dlaczego rozwiązuje problem, poprawiłoby długoterminową wartość odpowiedzi. Przeczytaj to .
Shanteshwar Inde

To nie jest HoC, to prosty komponent, który powiela funkcjonalność tego Fragmentkomponentu.
Emile Bergeron

4

Reakcja polega na tym, że wyrażenie JSX musi mieć dokładnie jeden najbardziej zewnętrzny element.

źle

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

poprawny

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

1

React 16 otrzymuje zwrot jako tablicę, więc powinien być zawinięty przez jeden element, taki jak div.

Niewłaściwe podejście

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

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

    );
}

Właściwe podejście (wszystkie elementy w jednym div lub innym używanym elemencie)

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

Składniki reagujące muszą być zapakowane w pojedynczy pojemnik, którym może być dowolny tag np. „<Div> .. </ div>”

Możesz sprawdzić metodę renderowania ReactCSSTransitionGroup


0

Zaimportuj widok i zawiń View. Zawijanie w divnie działało dla mnie.

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

2
to dlatego, że używasz reagowania natywnego.
Nick H247

Fragmenty są również dostępne w React Native, więc Viewnaprawdę nie jest najlepszym rozwiązaniem.
Emile Bergeron

0

Nieprawidłowy: nie tylko elementy potomne

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

Prawidłowy: element główny pod elementami potomnymi

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

Unikaj wpisywania odpowiedzi „ja też” lub powtarzania tego samego rozwiązania, chyba że masz coś istotnego do dodania .
Emile Bergeron

-1

Dla programistów Rect-Native. Wystąpił ten błąd podczas renderowaniaItem w FlatList. Miałem dwa komponenty tekstowe. Używałem ich jak poniżej

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

Ale po tym, jak położyłem te holownicze komponenty Inside View, zadziałało to dla mnie.

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

Być może korzystasz z innych komponentów, ale umieszczenie ich w widoku może być dla Ciebie odpowiednie.


Fragmenty są również dostępne w React Native, więc a Viewnie jest naprawdę najlepszym rozwiązaniem.
Emile Bergeron

-1

Myślę, że komplikacja może również wystąpić podczas próby zagnieżdżenia wielu elementów Div w instrukcji return. Możesz to zrobić, aby zapewnić renderowanie komponentów jako elementów blokowych.

Oto przykład poprawnego renderowania kilku komponentów przy użyciu wielu div.

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

-1

Miałem problem, który spowodował ten błąd, który nie był do końca oczywisty.

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

A oto poprawka ...

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

Domyśl. Informacje udostępniane tutaj na wypadek, gdyby inni uderzyli w ten guz. Najwyraźniej nie można mieć nazwy klasy elementów takiej samej jak nazwa funkcji nadrzędnej React.


To nieprawda. Jest to nawet ustawienie domyślne w szablonie React codeandbox. Coś innego musiało być w konflikcie z tą nazwą klasy.
Emile Bergeron
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.