React JSX - How to do it the right way - Part II

React JSX - How to do it the right way - Part II

In the previous part of React JSX series, we took a look at how to correctly loop through arrays and objects in React. In this article, we'll help you learn how to write conditional statements when rendering components.

Everything below applies to React Native as well!

Conditional statements inside React render

While learning to code in React, most of us have probably tried this and realized it won't work:

render() {
    return (
        <div>
            {
                // this won't work
            }
            {
                if (condition) {
                    // do something
                } else {
                    // do something else
                }
            }
        </div>
    )
}

JSX is just a syntactic sugar for function calls and object construction, and, although JavaScript is pretty flexible, we still can't pass the code like the if-else above as a parameter to a function.

Below are the correct ways to do the conditioning!

Let's say we have a flight object and in our Flight component, we want to show whether it's cancelled or not. The most usual way to do simple conditioning like this is via ternary expressions:

render() {
    return (
        <p>
            {
                flight.cancelled ?
                    'Cancelled'
                    :
                    'Regular'
            }
        </p>
    )
}

Ternary expressions are okay for simpler stuff but what happens if we have something more complex?

Let's say, what if we have a situation where we should use an else if statement?

Of course, we can do something like this:

render() {
    return (
        <p>
            {
                flight.cancelled ?
                    'Cancelled'
                    :
                    ( 
                       flight.arrived ?
                           'Arrived'
                           :
                           'Regular'
                    )
            }
        </p>
    )
}

This can easily pile up and start looking pretty messy and unreadable. What's the better way then? Do the conditioning above the return statement:

render() {
    let status;
    if (flight.cancelled) {
        status = 'Cancelled';
    } else if (flight.arrived) {
        status = 'Arrived';
    } else {
        status = 'Regular';
    }
    return (
        <p>
            { status }
        </p>
    )
}

Now, in previous examples, we were only rendering a status in our component, but usually, there will be much more to render. So, if we want to render the departure time, the destination, the arrival time and status, for example, it might look something like this:

render() {
    let status;
    if (flight.cancelled) {
        status = 'Cancelled';
    } else if (flight.arrived) {
        status = 'Arrived';
    } else {
        status = 'Regular';
    }
    return (
        <div>
            <p>
                Destination: { flight.destination }
            </p>
            <p>
                Departure time: { flight.departureTime }
            </p>
            <p>
                Arrival time: { flight.arrivalTime }
            </p>
            <p>
                Flight status: { status }
            </p>
        </div>
    )
}

Now, this is okay, it's working, but we're polluting the render method of a component. Imagine we had more if-else statements - it'd be a mess.
Instead, why wouldn't we move it outside of a render method, so it's completely separate?


renderStatus() {
    let status;
    if (flight.cancelled) {
        status = 'Cancelled';
    } else if (flight.arrived) {
        status = 'Arrived';
    } else {
        status = 'Regular';
    }
    return status;
}

render() {
    return (
        <div>
            <p>
                Destination: { flight.destination }
            </p>
            <p>
                Departure time: { flight.departureTime }
            </p>
            <p>
                Arrival time: { flight.arrivalTime }
            </p>
            <p>
                Flight status: { this.renderStatus() }
            </p>
        </div>
    )
}

Our code looks way neater now, right?

All of the examples above were about rendering a simple string based on some boolean values but what could have happened if we had to add a different class, or pass a different prop? Logic is the same.

Let's say we have a button to book a flight unless it's cancelled:

renderButton() {
    let handleOnPress;
    let buttonClassName;
    let buttonText;
    if (flight.cancelled) {
        buttonClassName = 'button disabled';
        buttonText = 'Cancelled';
    } else {
        buttonClassName = 'button';
        buttonText = 'Book';
        handleOnPress = this.bookFlight
    }
    return (
        <button
            className={buttonClassName}
            onPress={handleOnPress}
        >
            { buttonText }
        </button>
    )
}

We can also use ternary expressions to have the same result as the one produced by code above:

renderButton() {
    // for className, you can also use this:
    // `button ${flight.cancelled ? 'disabled' : ''}`
    return (
        <button
            className={flight.cancelled ? 'button disabled' : 'button'}
            onPress={flight.cancelled ? null : this.bookFlight}
        >
            { 
                flight.cancelled ?
                    'Cancelled'
                    :
                    'Book'
            }
        </button>
    )
}

If you just want to render/pass something if the condition is fullfilled, you can also write it this way:

render() {
    return (
        <p>
            {
                // condition && what_to_render
            }
            { 
                flight.cancelled && 'Cancelled'
            }
        </p>
    )
}

Conditional rendering inside for loop

When rendering a list of items, you might want to render them differently based on some condition. For example, you might want to add grey background to all the even items. How to do this? You can either use ternary expression or standard if/else, both will work! Remember that it's a function like any other! Here's a small example:

render() {
  	return (
    	<div>
    		{
      			this.state.list.map((item, index) => {
                    // if even, render grey background
                    if (index % 2 === 0) {
                      // don't forget to return what you want to render!
                      return (
                        <div style={{backgroundColor: 'grey'}}>
                          {item.name}
                        </div>
                      );
                    } else {
                      // you can also use ternary expression
                      return (
                        <div style={item.expired ? {backgroundColor: 'red'} : null}>
                          {item.name}
                        </div>
                      );
                    }
                  })
    		}
    	</div>
	);
}

Of course, this mapping function can be extracted outside of render method, for increased readability.

Switch statement

We've been talking about if-else, but conditioning can, also, be done with a switch statement. Let's say that, instead of the boolean attributes for cancelled and arrived, we have one status attribute. Of course, we can't just type something like this:

render() {
    return (
        <p>
            {
                // this will raise an error
            }
            { 
                switch(flight.status) {
                    case 'cancel':
                        return "Cancelled";
                    case 'arrive':
                        return "Arrived";
                    default:
                        return "Regular";
                }
            }
        </p>
    )
}

For switch, there's no neat way to do it directly in return statement. We can, of course, use immediately-invoked function containing a switch, but it's neither practical nor does it look nice. To make the switch above work, just move it to some function outside of render method:

renderStatus() {
    switch(flight.status) {
        case 'cancel':
            return "Cancelled";
        case 'arrive':
            return "Arrived";
        default:
            return "Regular";
    }
}
render() {
    return (
        <p>
            { 
                this.renderStatus()
            }
        </p>
    )
}

Of course, switch can also be specified in render method, above the return statement. Just remember not to use 'return' in cases, but a 'break':

render() {
    let status;
    switch(flight.status) {
        case 'cancel':
            status = "Cancelled";
            break;
        case 'arrive':
            status = "Arrived";
            break;
        default:
            status = "Regular";
            break;
    }
    return (
        <p>
            { status }
        </p>
    )
}

Now you're completely ready to do conditioning in React! Go ahead and try these out, make that code you've been struggling with for hours finally work properly!

If you already knew how to use if-else and switch in React, we hope you still enjoyed the article and freshened up your knowledge a bit.

Thank you for your time!

If you liked this article, make sure to subscribe! Next time, we'll be talking about props!