0

Learning a bit of React but it seems to me like there's a conditional rendering bug with React itself.

Suppose I have a Foo component like so:

foo.js

import React, { Component } from 'react';

class Foo extends Component {
  render() {
    const isLoggedIn = this.props.isLoggedIn;

    return(
      <div>
        { isLoggedIn ? (
          <div>one</div><div>two</div>
        ) : (
          <div>one</div><div>two</div><div>three</div>
        )}
      </div>
    );
  }
}

export default Foo;

and I use it like so:

app.js

import React, { Component } from 'react';
import Foo from './components/foo';

class App extends Component {
  render() {
    return (
      <div>
          <Foo isLoggedIn={false} />
      </div>
    );
  }
}

export default App;

This produces the error:

Syntax error: Adjacent JSX elements must be wrapped in an enclosing tag

Please note the above Foo component, there is only a single parent div being returned not array. If it was an array, then yes I agree with the error.

The official example given in the React document's example is like this:

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn ? (
        <LogoutButton onClick={this.handleLogoutClick} />
      ) : (
        <LoginButton onClick={this.handleLoginClick} />
      )}
    </div>
  );
}

https://reactjs.org/docs/conditional-rendering.html

Does this look like a bug in React to anyone?

Update

Based on the answers and comments given here, the implied behaviour of React is ternary operators inside the render() function comes with it's own render calls behind the scenes, acting like a virtual component, which would mean an extra layer of <div> needs to be wrapped around the list of my child elements.

Emberjs Foo component

My confusion arise from the fact I have done some Emberjs development in the past and a component like this works as expected:

<h3>Foo component</h3>

{{#if isLoggedIn}}
  <div>one</div><div>two</div>
{{else}}
  <div>one</div><div>two</div><div>three</div>
{{/if}}

Thanks for the explanation from everyone nonetheless.

Zhang
  • 11,549
  • 7
  • 57
  • 87
  • I am not returning a list of element Shubham, I am returning a single parent div node with child elements inside, which according to React's doc is perfectly fine. – Zhang Mar 30 '18 at 09:17
  • I guess you are returnign multiple elements, may not be from render but in the conditional render here `
    one
    two
    `
    – Shubham Khatri Mar 30 '18 at 09:23
  • I come from a different background but I did not know a ternary operator in React is treated as another "virtual component" with it's own render call behind the scenes. – Zhang Mar 30 '18 at 09:29
  • yes they are treated as separate block of JSX elements. I marked it as a duplicate considering the Other question solved your problem – Shubham Khatri Mar 30 '18 at 10:21

5 Answers5

1

You have a syntax error

          <div>one</div><div>two</div><div>three</div

should be

          <div>one</div><div>two</div><div>three</div>
Kerry Gougeon
  • 1,317
  • 12
  • 18
1

Did you try adding ?

return(
      <div>
        { isLoggedIn ? (
          <Fragment><div>one</div><div>two</div><Fragment>
        ) : (
          <Fragment><div>one</div><div>two</div><div>three</div><Fragment>
        )}
      </div>
    );
benjamin Rampon
  • 1,316
  • 11
  • 18
  • Fragment also work like ivp's answer of wrapping extra `
    ` around the list of elements but real question is why this is necessary ?
    – Zhang Mar 30 '18 at 09:15
  • Just a note: Fragment is also a right answer. However, the concept of Fragments was not present in earlier release. Hence will not work with code using older react version. – Ishwar Patil Mar 30 '18 at 09:18
1

You are returning the 2 or 3 divs in the condition. Instead you should wrap them into on div and return.

Notice the wrapper div below.

{ isLoggedIn ? (
      <div className='wrapper'><div>one</div><div>two</div><div>
    ) : (
      </div className='wrapper'><div>one</div><div>two</div><div>three</div></div>
    )}

Also note that there is small typo below

<div>one</div><div>two</div><div>three</div
Ishwar Patil
  • 1,671
  • 3
  • 11
  • 19
  • This does seem to fix the issue if I wrap another layer of div inside multiple elements but what I want to know why it's necessary? – Zhang Mar 30 '18 at 09:14
  • 1
    One simple rule in React. Wherever you are returning, always return on element, not multiple elements. In your example you are returning 2 divs on true conditions and 3 divs in false condition. – Ishwar Patil Mar 30 '18 at 09:16
  • @Zhang, I hope this explaination helps. :) – Ishwar Patil Mar 30 '18 at 09:20
  • So just one of those ins-and-outs of React that isn't documented somewhere but learnt from the hard way? – Zhang Mar 30 '18 at 09:20
  • I cannot comment on the complete react documentation as I haven't read each and every line of it :) . But whatever I have read, the quality is very good and I am pretty sure that this is mentioned somewhere in documentation (may be indirectly). – Ishwar Patil Mar 30 '18 at 09:23
  • And I think the error message is very direct and simple to understand. May be some debugging can be handy. :) – Ishwar Patil Mar 30 '18 at 09:25
  • I understand what the error means just the other hidden knowledge and understanding about React is what got me. Thank you for the answer nonetheless. – Zhang Mar 30 '18 at 09:52
  • Happy to help!!! – Ishwar Patil Mar 30 '18 at 10:04
1

Syntax error: Adjacent JSX elements must be wrapped in an enclosing tag?

you are returning multiple sibling JSX elements in an incorrect manner.

In Foo:

return(
      <div>
        { isLoggedIn ? (
          <div>one</div> //are siblings without 
          <div>two</div> //wrapping in container element.
        ) : (
          <div>one</div> //are siblings without 
          <div>two</div> //wrapping in 
          <div>three</div>//container element.
        )}
      </div>
    );

Right approach :

return (
            <div>
                {isLoggedIn
                    ? (
                        <div> //add wrapper
                             /...
                        </div>
                    )
                    : (
                        <div> //add wrapper
                            //...
                        </div>
                    )}
            </div>
        );

Or

If you are using React16 then you can use React.Fragement as well:

e.g.

<React.Fragment>
  <div>one</div>
  <div>two</div>
</React.Fragment>
RIYAJ KHAN
  • 15,032
  • 5
  • 31
  • 53
0

You need to wrap the element rendered in the condition

return(
  <div>
    { isLoggedIn ? (
      <div><div>one</div><div>two</div></div>
    ) : (
      <div><div>one</div><div>two</div><div>three</div></div>
    )}
  </div>
);

Note the extra div around the nested conditional elements

Mohit Mutha
  • 2,921
  • 14
  • 25