86

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?

Liam
  • 27,717
  • 28
  • 128
  • 190
Wordpressor
  • 7,173
  • 23
  • 69
  • 108

8 Answers8

128

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 }
Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
Mayank Shukla
  • 100,735
  • 18
  • 158
  • 142
  • Thanks! Not sure why, but arrays never worked for me - it always returned the same error. – Wordpressor Feb 22 '18 at 15:55
  • 1
    For those who have the same error in react native... you probably have multiple elements in the ternary like this: `{ this.state.isEnabled ? Hello WorldI am here : Hello WorldI am on my way } ` So all you need to do is put the elements into a `{this.state.isEnabled ? Hello WorldI am here : Hello WorldI am on my way }` Hope this helps – aps5842 Jun 20 '18 at 18:30
  • Nice !! I had this error and wasnt understanding why. Its due to the way you're suppose to return elements. Thanks for pointing fragment ! – Baldráni Jun 01 '19 at 22:30
  • 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. – Aaron Franke Dec 17 '20 at 05:57
  • NOTE! React.Fragment is a bit tricky. If you have another route after the fragment, it may not catch it. Check this https://github.com/ReactTraining/react-router/issues/6953 – Mahmoud Moravej May 31 '21 at 18:47
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} />
  </>
}
Eoin Traynor
  • 600
  • 4
  • 8
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 }
Alexandre Soria
  • 161
  • 1
  • 3
  • You just saved my life! Tried to implement NextJS's Image component and got this error. Wrapping the Image<> inside `<> ... >` fixed the issue! – Dentrax Feb 21 '22 at 19:54
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>}
Ritwik
  • 71
  • 1
  • 3
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

Tom Smykowski
  • 25,487
  • 54
  • 159
  • 236
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>
    );
  }
}
  • 1
    This worked great, thanks. Worth noting that it requires an "import View from 'react-view-component/lib/View';" – nwhaught Mar 04 '19 at 16:14
  • 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 (
    ); } }`
    – Jeph Dec 24 '19 at 21:01
2

This one works for me.

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

BMA88
  • 329
  • 2
  • 2
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>
Alisson Reinaldo Silva
  • 10,009
  • 5
  • 65
  • 83