ReactJs - Creating an "If" component... a good idea?

JavascriptReactjs

Javascript Problem Overview


I've read in the React docs that "if" type statements can't be used in JSX code, because of the way JSX renders into javascript, it doesn't work out as one would expect.

But is there any reason why implementing an "if" component is a bad idea? It seems to work fine from my initial tests, and makes me wonder why this isn't done more often?

Part of my intent is to allow react development to be as much as possible, markup based - with as little javascript as possible. This approach, to me, feels like more of a "data driven" approach.

You can check it out here on JS Fiddle

<script type='text/javascript' src="https://unpkg.com/[email protected]/dist/JSXTransformer.js"></script>
<script type='text/javascript' src="https://unpkg.com/[email protected]/dist/react-with-addons.js"></script>

<script type="text/jsx">
/** @jsx React.DOM */
    
var If = React.createClass({
  displayName: 'If',

  render: function()
  {
    if (this.props.condition)
      return <span>{this.props.children}</span>
    return null;
  }
});

var Main = React.createClass({
    render: function() {
        return (
           <div>
             <If condition={false}>
                <div>Never showing false item</div>
             </If>
             <If condition={true}>
                <div>Showing true item</div>
             </If>
          </div>
        );
    }
});

React.renderComponent(<Main/>, document.body);
</script>

Running the above results in:

> Showing true item

Javascript Solutions


Solution 1 - Javascript

Check out the If-Else in JSX section in the react Docs.

In JSX, you can't put statements inside curly braces--only expressions. If you don't know the difference between expressions vs statements in JavaScript read this article. This limitation is because JSX desugars into function calls and you can't use if-statements as arguments to a function in JavaScript. However, you can use boolean operators (&&, || and ? :) to do a similar job. They are expressions so they can fit inside the constructor calls JSX generates and their short-circuiting evaluation is the same as the one used in if statements.

<div>
    {(true
        ? <div>Showing true item</div>     
        : <div>Never showing false item</div>
    )}
</div>
<p>My name is {this.name || "default name"}</p>

Additionally, React will treat null and false as an "empty component" that does not get rendered in the real DOM (currently it uses that same noscript trick behind the scenes). This is useful when you don't want an "else" branch. See False in JSX for details.

<div>
    {shouldIncludeChild ? <ChildComponent/> : false}
</div>

As for the If component you asked about, one problem it has is that in its current form it will evaluate its children even if the condition is false. This can lead to errors when the body of the If only makes sense if the condition is true:

<If condition={person !== null}>
    //This code throws an exception if this.person is null
    <div>{person.name}</div>
</If>

You could workaround this by having the if component receive the body as a function instead of as a list of child components but its more verbose:

<If condition={person !== null} body={function(){
    return <div>{person.name}</div>
}/>

Finally, since the If component is stateless, you should consider using a plain function instead of a new component class, as this would make the "If" transparent to React's reconciliation algorithm. If you use an If component, then a <div> and a <If><div> will be considered incompatible and React will do a full redraw instead of trying to merge the new component with the old one.

// This custom if function is for purely illustrative purposes
// However, this idea of using callbacks to represent block of code
// is useful for defining your own control flow operators in other circumstances.
function myCustomIf(condition, onTrue, onFalse){
    onTrue  = onTrue  || function(){ return null }        
    onFalse = onFalse || function(){ return null }
    if(condition){
        return onTrue();
    }else{
        return onFalse();
    }
}

<div>    
    {myCustomIf(person !== null, function(){
         return <div>{person.name}</div>
     })}
</div>

Solution 2 - Javascript

You don't need anything than plain JS.

Safe and readable (but verbose)

maybeRenderPerson: function() {
    var personName = ...;
    if ( personName ) {
        return <div className="person">{personName}</div>;
    }
}

render: function() {
    return (
       <div className="component">
          {this.maybeRenderPerson()}
       </div>
    );
}

It is a bit verbose, but it permits to easily split your logic in smaller, focused blocks. When components start to become complex, this is the most readable.


Concise and readable (but dangerous)

render: function() {
    var personName = ...; // present or absent name, but never ""
    return (
       <div className="component">
          {personName && (
            <div className="person">{personName}</div>
          )}
       </div>
    );
}

This syntax can be quite dangerous if the tested variable can be falsy values like 0,"" or false. Particularly with numbers you should rather modify the test slightly if you want to make sure it renders for 0:

render: function() {
    var counter= ...; // number, can be 0
    return (
       <div className="component">
          {(typeof counter !== 'undefined') && (
            <div className="counter">{counter}</div>
          )}
       </div>
    );
}

When components become complex, splitting into multiple smaller components, and having code-style conventions can help to keep it readable:

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

Modern syntax (but too early)

The do notation with functional stateless components can be useful for expressiveness without loosing readability. You can easily split a large component into very small and focused ones that use the do notation:

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

It is a bit like using the module pattern inside JSX, so it's quite flexible, but with a lot less boilerplate.

To enable this, you need ES7 stage 0 compilation tooling, and support from your favorite IDE might not exist yet.

Solution 3 - Javascript

You can do inline conditionals like this

{true && (
<div>render item</div>
)}

{false && (
<div>don't render item</div>
)}

Or you can just use a var

var something;

if (true) {
something = <div>render item</div>
}

{something}

Solution 4 - Javascript

You can do a self-executing function, with the right context too (with the help of Babel though)! Which I prefer because there's no need for assigning of variables and you can be as complex as you want (though you probably shouldn't for the sake of maintainability):

render() {
  return (
    <div>
      <div>Hello!</div>
      {() => {
        if (this.props.isLoggedIn) {
          return <Dashboard />
        } else {
          return <SignIn />
        }
      }()}
      <div>Another Tag</div>
    </div>
  );
}

   

Solution 5 - Javascript

The experimental ES7 do syntax is really nice for this situation. If you're using Babel, enable the es7.doExpressions feature then:

render() {
  var condition = false;

  return (
    <div>
      {do {
        if (condition) {
          <span>Truthy!</span>;
        } else {
          <span>Not truthy.</span>;
        }
      }}
    </div>
  );
}

See http://wiki.ecmascript.org/doku.php?id=strawman:do_expressions

Solution 6 - Javascript

I think your main question is not "Can I implement an If component" (because clearly you can), but "Is there any reason why implementing an If component is a bad idea?"

The main reason it's a bad idea is because an adds component render cycles to what would otherwise be a small amount of code in an existing component. The extra component wrapper means that React needs to run an entire extra render cycle on its children. This is not a big deal if the If component is only used in one place, but if your app is littered with them, you've just made your UI that much slower.

Similar to how: <div>Hi</div> is more efficient than <div><div>Hi</div></div>.

Solution 7 - Javascript

ES7 do closure is great for that:

For example, if you're using webpack - you can add ES7 features:

Firstly install stage-0 features with:

npm install babel-preset-stage-0 -save-dev;

Then in webpack loaders settings, add stage-0 babel preset:

	loaders : [
        //...
		{
			test : /\.js$/,
			loader : "babel-loader",
			exclude : /node_modules/,
			query : {
				presets : ["react", "stage-0", "es2015"]
			}
		}
    ]

Then your component render function can look like:

import React , { Component } from "react";

class ComponentWithIfs extends Component {

	render() {
		return (
			<div className="my-element">
				{
					do {
						if ( this.state.something ) {
							<div>First Case</div>
						} else {
							<div>2nd Case</div>
						}
					}
				}
			</div>
		);
	}
}

Solution 8 - Javascript

I think it depends on the case. I just had the same question that why I'm here. I just think that if the component to render is huge, it can be usefull to have an IF component instead of the standard conditional rendering method.

...
<IF condition={screenIs("xs")}>
       <div> Then statement</div>
       <div>other Statement</div>
       <div>and so on</div>
</IF>
...

is definitively more readable than

...
{
  screenIs("xs") &&  (
     <Fragment>
         <div> Then statement</div>
         <div>other Statement</div>
         <div>and so on</div>
      </Fragment>
    )
 }
 ...

also for ternary statement,

...
<IF.Ternary condition={screenIs("xs")}>
   <Fragment>
       <div> Then statement</div>
       <div>other then Statement</div>
       <div>and so on</div>
   </Fragment>
   <Fragment>
       <div> Else statement</div>
       <div>other Else Statement</div>
       <div>and so on</div>
   </Fragment>
 </IF.Ternary>
 ...

is more readable than

...
{
  screenIs("xs")
   ? (
     <Fragment>
         <div> Then statement</div>
         <div>other Then Statement</div>
         <div>and so on</div>
      </Fragment>
    )
    : (
     <Fragment>
         <div> Else statement</div>
         <div>other Else Statement</div>
         <div>and so on</div>
      </Fragment>
    )
 }
 ...

anyway, to make it more readable, you can just define your conditional rendering part in some method and call it in your main render

{
  ...
  renderParts = () => {
    return screenIs('xs') ? <div>XS</div> : <div>Not XS</div>
  }
  ...
  render() {
    return (
      ...
      {this.renderParts()}
      ...
    )
  }
 }

Solution 9 - Javascript

If anyone is interested, I just released a react module for these purposes:

var Node = require('react-if-comp');
...
React.createClass({
    render: function() {
        return (
            <div>
                <Node if={false} else={<div>Never showing false item</div>} />
                <Node if={true} then={<div>Showing true item</div>} />
            </div>
        );
    }
});

Solution 10 - Javascript

I would try to avoid conditional rendering with "templates" altogether. In React and JSX you can use the full set of JavaScript functionality to make conditional rendering happen:

  • if-else
  • ternary
  • logical &&
  • switch case
  • enums

That's what makes the rendering with JSX so powerful. Otherwise one could invent again own templating languages that are library/framework specific like in Angular 1/2.

Solution 11 - Javascript

For maintainable code skip unnecessary abstractions like If and add some control logic with an early termination before the return statement in your render method, e.g.

import React from 'react-native';

let {
  Text
} = React;

let Main = React.createClass({
  setInitialState() {
    return { isShown: false }
  },
  render() {
    let content;
    if (this.state.isShown) {
      content = 'Showing true item'
    } else {
      return false;
    }
    return (
      <Text>{content}</Text>
    );
  }
});

Keeps you DRY. Lasting protection.

Solution 12 - Javascript

render-if is a light weight function that will render an element if a condition is satisfied

As an in-line expression
class MyComponent extends Component {
  render() {
    return (
      {renderIf(1 + 2 === 3)(
        <span>The universe is working</span>
      )}
    );
  }
}
As a named function
class MyComponent extends Component {
  render() {
    const ifTheUniverseIsWorking = renderIf(1 + 2 === 3);
    return (
      {ifTheUniverseIsWorking(
        <span>The universe is still wroking</span>
      )}
    )
  }
}
As a composed function
const ifEven = number => renderIf(number % 2 === 0);
const ifOdd = number => renderIf(number % 2 !== 0);

class MyComponent extends Component {
  render() {
    return (
      {ifEven(this.props.count)(
        <span>{this.props.count} is even</span>
      )}
      {ifOdd(this.props.count)(
        <span>{this.props.count} is odd</span>
      )}
    );
  }
}

Solution 13 - Javascript

For me is enough taking advantage of the lazy evaluation in a logic expression if no else statement is needed:

render() {
    return(
        <div>
             {
                 condition &&
                     <span>Displayed if condition is true</span>
             }
        </div>
    );    
}

Or the ternay operator if an else is needed:

render() {
    return(
        <div>
             {
                 condition ?
                     <span>Displayed if condition is true</span>
                          :
                     <span>Displayed if condition is false</span>
             }
        </div>
    );    
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBrad ParksView Question on Stackoverflow
Solution 1 - JavascripthugomgView Answer on Stackoverflow
Solution 2 - JavascriptSebastien LorberView Answer on Stackoverflow
Solution 3 - JavascriptChad SciraView Answer on Stackoverflow
Solution 4 - JavascriptrclaiView Answer on Stackoverflow
Solution 5 - JavascriptbalexandView Answer on Stackoverflow
Solution 6 - JavascriptEv HausView Answer on Stackoverflow
Solution 7 - JavascriptAdam PietrasiakView Answer on Stackoverflow
Solution 8 - Javascriptuser3550446View Answer on Stackoverflow
Solution 9 - JavascriptGordon FreemanView Answer on Stackoverflow
Solution 10 - JavascriptRobin WieruchView Answer on Stackoverflow
Solution 11 - JavascriptvhsView Answer on Stackoverflow
Solution 12 - JavascriptAtticusView Answer on Stackoverflow
Solution 13 - JavascriptJosé Antonio PostigoView Answer on Stackoverflow