Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have conditional elements and keep DRY with Facebook React's JSX?

People also ask

How do you conditionally render in JSX?

Conditional rendering in React works the same way conditions work in JavaScript. Use JavaScript operators like if or the conditional operator to create elements representing the current state, and let React update the UI to match them. This example renders a different greeting depending on the value of isLoggedIn prop.

How do you add an if-else condition in JSX?

We can embed any JavaScript expression in JSX by wrapping it in curly braces. But only expressions not statements, means directly we can not put any statement (if-else/switch/for) inside JSX.

Can you nest JSX elements into another JSX element?

You can nest JSX elements inside of other JSX elements, just like in HTML.

Do JSX elements need to be capitalized?

User-Defined Components Must Be Capitalized createElement(Foo) and correspond to a component defined or imported in your JavaScript file. We recommend naming components with a capital letter. If you do have a component that starts with a lowercase letter, assign it to a capitalized variable before using it in JSX.


Just leave banner as being undefined and it does not get included.


What about this. Let's define a simple helping If component.

var If = React.createClass({
    render: function() {
        if (this.props.test) {
            return this.props.children;
        }
        else {
            return false;
        }
    }
});

And use it this way:

render: function () {
    return (
        <div id="page">
            <If test={this.state.banner}>
                <div id="banner">{this.state.banner}</div>
            </If>
            <div id="other-content">
                blah blah blah...
            </div>
        </div>
    );
}

UPDATE: As my answer is getting popular, I feel obligated to warn you about the biggest danger related to this solution. As pointed out in another answer, the code inside the <If /> component is executed always regardless of whether the condition is true or false. Therefore the following example will fail in case the banner is null (note the property access on the second line):

<If test={this.state.banner}>
    <div id="banner">{this.state.banner.url}</div>
</If>

You have to be careful when you use it. I suggest reading other answers for alternative (safer) approaches.

UPDATE 2: Looking back, this approach is not only dangerous but also desperately cumbersome. It's a typical example of when a developer (me) tries to transfer patterns and approaches he knows from one area to another but it doesn't really work (in this case other template languages).

If you need a conditional element, do it like this:

render: function () {
    return (
        <div id="page">
            {this.state.banner &&
                <div id="banner">{this.state.banner}</div>}
            <div id="other-content">
                blah blah blah...
            </div>
        </div>
    );
}

If you also need the else branch, just use a ternary operator:

{this.state.banner ?
   <div id="banner">{this.state.banner}</div> :
   <div>There is no banner!</div>
}

It's way shorter, more elegant and safe. I use it all the time. The only disadvantage is that you cannot do else if branching that easily but that is usually not that common.

Anyway, this is possible thanks to how logical operators in JavaScript work. The logical operators even allow little tricks like this:

<h3>{this.state.banner.title || 'Default banner title'}</h3>

Personally, I really think the ternary expressions show in (JSX In Depth) are the most natural way that conforms with the ReactJs standards.

See the following example. It's a little messy at first sight but works quite well.

<div id="page">
  {this.state.banner ? (
    <div id="banner">
     <div class="another-div">
       {this.state.banner}
     </div>
    </div>
  ) : 
  null} 
  <div id="other-content">
    blah blah blah...
  </div>
</div>

You may also write it like

{ this.state.banner && <div>{...}</div> }

If your state.banner is null or undefined, the right side of the condition is skipped.


The If style component is dangerous because the code block is always executed regardless of the condition. For example, this would cause a null exception if banner is null:

//dangerous
render: function () {
  return (
    <div id="page">
      <If test={this.state.banner}>
        <img src={this.state.banner.src} />
      </If>
      <div id="other-content">
         blah blah blah...
      </div>
    </div>
  );
}

Another option is to use an inline function (especially useful with else statements):

render: function () {
  return (
    <div id="page">
      {function(){
        if (this.state.banner) {
          return <div id="banner">{this.state.banner}</div>
        }
      }.call(this)}
      <div id="other-content">
         blah blah blah...
      </div>
    </div>
  );
}

Another option from react issues:

render: function () {
  return (
    <div id="page">
      { this.state.banner &&
        <div id="banner">{this.state.banner}</div>
      }
      <div id="other-content">
         blah blah blah...
      </div>
    </div>
  );
}

Simple, create a function.

renderBanner: function() {
  if (!this.state.banner) return;
  return (
    <div id="banner">{this.state.banner}</div>
  );
},

render: function () {
  return (
    <div id="page">
      {this.renderBanner()}
      <div id="other-content">
        blah blah blah...
      </div>
    </div>
  );
}

This is a pattern I personally follow all the time. Makes code really clean and easy to understand. What's more it allows you to refactor Banner into its own component if it gets too large (or re-used in other places).


&& + code-style + small components

This simple test syntax + code-style convention + small focused components is for me the most readable option out there. You just need to take special care of falsy values like false, 0 or "".

render: function() {
    var person= ...; 
    var counter= ...; 
    return (
       <div className="component">
          {person && (
            <Person person={person}/>
          )}
          {(typeof counter !== 'undefined') && (
            <Counter value={counter}/>
          )}
       </div>
    );
}

do notation

ES7 stage-0 do notation syntax is also very nice and I'll definitively use it when my IDE supports it correctly:

const Users = ({users}) => (
  <div>
    {users.map(user =>
      <User key={user.id} user={user}/>
    )}
  </div>
)  

const UserList = ({users}) => do {
  if (!users) <div>Loading</div>
  else if (!users.length) <div>Empty</div>
  else <Users users={users}/>
}

More details here: ReactJs - Creating an "If" component... a good idea?