Why do I get the error "expressions must have one parent element", how do I fix this?
Asked Answered
C

8

91

I'm relatively new to React and I'm wondering what's the standard here.

Imagine I have a react-router like this one:

<Router history={history}>
    <Route path="/" component={App}>
      <Route path="home component={Home} />
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox} />
      <Route path="contacts" component={Contacts} />
    </Route>
</Router>

And now I want to remove two routes if prop.mail is set to false, so a sane way of doing that would look like this:

<Router history={history}>
      <Route path="/" component={App}>
        <Route path="home component={Home} />
        <Route path="about" component={About} />

        { if.this.props.mail ? 
          <Route path="inbox" component={Inbox} />
          <Route path="contacts" component={Contacts} />
        : null }

      </Route>
 </Router>

But there are 2 routes and React returns error:

expressions must have one parent element.

I don't want to use multiple ifs here. What's the preferred React way of handling this?

Cannibalism answered 20/2, 2018 at 13:45 Comment(0)
S
131

Put them in an array (assign the keys also):

{ if.this.props.mail ? 
    [
        <Route key={0} path="inbox" component={Inbox} />,
        <Route key={1} path="contacts" component={Contacts} />
    ]
: null }

With latest React version, you can try React.Fragment also, like this:

{ if.this.props.mail ? 
    <React.Fragment>
        <Route path="inbox" component={Inbox} />,
        <Route path="contacts" component={Contacts} />
    </React.Fragment>
: null }
Sc answered 20/2, 2018 at 13:50 Comment(5)
Thanks! Not sure why, but arrays never worked for me - it always returned the same error.Cannibalism
For those who have the same error in react native... you probably have multiple elements in the ternary like this: { this.state.isEnabled ? <Text>Hello World</Text><Text>I am here</Text> : <Text>Hello World</Text><Text>I am on my way</Text> } So all you need to do is put the elements into a {this.state.isEnabled ? <View><Text>Hello World</Text><Text>I am here</Text></View> : <View><Text>Hello World</Text><Text>I am on my way</Text></View> } Hope this helpsHeadmost
Nice !! I had this error and wasnt understanding why. Its due to the way you're suppose to return elements. Thanks for pointing fragment !Baalman
What would the complete code look like? I'm not sure how to take this fragment and add it as a child of the parent element.Rosebay
NOTE! React.Fragment is a bit tricky. If you have another route after the fragment, it may not catch it. Check this github.com/ReactTraining/react-router/issues/6953Paroxysm
A
49

You can leverage short hand fragments to return a list of children along with Logical '&&' Operator for conditional rendering. Nice and clean! 😄

{this.props.mail && 
  <>
    <Route path="inbox" component={Inbox} />
    <Route path="contacts" component={Contacts} />
  </>
}
Aleurone answered 29/10, 2019 at 14:9 Comment(2)
the , is not necessary, correct?Bore
Thanks for flagging @EinEsellesEniE. Updated!Aleurone
L
16

You must been use a fragment tag e.g(div, <>,...).

Check this short solution:

{ if.this.props.mail ? 
 <>
   <Route path="inbox" component={Inbox} />
   <Route path="contacts" component={Contacts} />
 </>
 : null }
Leath answered 26/1, 2020 at 3:25 Comment(1)
You just saved my life! Tried to implement NextJS's Image component and got this error. Wrapping the Image<> inside <> ... </> fixed the issue!Strumpet
K
6

just try enclosing the code after the return statement in an element like <div>....code </div>,etc.

eg:-

const Div =()=>{
           return
            <div>
              <Button name="Save" ></Button>
              <Button name="Edit"></Button>
              <Button name="Cancel"></Button>  
            </div>}
Kwashiorkor answered 2/2, 2020 at 23:56 Comment(0)
D
4

2020 update

I have checked out every solution from answers. Here is the breakdown for regular React:

1. React Fragment

When i wanted to use it once, without adding additional DOM node - it worked. When i tried to use second React.Fragment got really bad errors. Wasn't able to fix it.

2. View

I was unable to import View properly. I don't know if this is only for Reactjs, or Native, but this does not work

3. Div

What actually worked was to put HTML into Div

Doley answered 24/8, 2020 at 10:33 Comment(0)
T
3

This one works for me.

<React.Fragment> …….. </React.Fragment>

Tine answered 6/11, 2020 at 16:11 Comment(0)
G
2

Faced the same error in a similar situation (React Native).

export default class App extends React.Component {
  render() {
    return (
      <StatusBar barStyle="default" />
      <AppContainer />
    );
  }
}

As indicated in the error prompt the JSX expression requires to have one parent element, hence wrap the elements in the return expression with a parent element. The flex: 1 style was added to allow the <View> element assume the height of the entire screen.

export default class App extends React.Component {
  render() {
    return (
      <View style={{flex: 1}}>
        <StatusBar barStyle="default" />
        <AppContainer />
      </View>
    );
  }
}
Girt answered 12/2, 2019 at 10:42 Comment(2)
This worked great, thanks. Worth noting that it requires an "import View from 'react-view-component/lib/View';"Hengelo
To add on, if we are talking normal React then you'd just want to wrap the above code in a div to prevent this error. Example: export default class App extends React.Component { render() { return ( <div> <StatusBar barStyle="default" /> <AppContainer /> </div> ); } }Plugboard
D
0

If you're using <Switch>, then using <div> and <React.Fragment> to wrap your routes will break it.

I like the idea of a <ProtectedRoute> component:

import { Component } from 'react';
import { Redirect, Route } from 'react-router-dom';

class ProtectedRoute extends Component<any> {
  render() {
    const { component: Component, allow, ...props } = this.props;
    if (!allow) {
      return <Redirect to={{ pathname: '/signin' }} />;
    }
    return <Route {...props} render={(props) => <Component {...props} />} />;
  }
}

export default ProtectedRoute;

Then use it like below:

<Router history={history}>
  <Route path="/" component={App}>
    <Route path="home" component={Home} />
    <Route path="about" component={About} />

    <ProtectedRoute path="inbox" component={Inbox} allow={this.props.mail} />
    <ProtectedRoute path="contacts" component={Contacts} allow={this.props.mail} />

  </Route>
</Router>
Despair answered 19/5, 2021 at 15:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.