Conditional Rendering in React using Ternaries and Logical AND
There are several ways that your React component can decide what to render. You can use the traditional if statement or the switch statement. In this article, we'll explore a few alternatives. But be warned that some come with their own gotchas if you're not careful.
Ternary vs if/else
Let's say we have a component that is passed a name prop. If the string is non-empty, we display a greeting. Otherwise, we tell the user they need to sign in.
Here's a Function Component that does just that.
const MyComponent = ({ name }) => {
if (name) {
return <div className="hello">Hello {name}</div>;
}
return <div className="hello">Please sign in</div>;
};
Pretty straightforward, but we can do better. Here's the same component written using a conditional ternary operator.
const MyComponent = ({ name }) => {
const message = name ? `Hello ${name}` : 'Please sign in';
return <div className="hello">{message}</div>;
};
Notice how concise this code is compared to the example above. Using a ternary allowed us to DRY up the duplicate <div className="hello">
markup. 🎉
Ternary vs Logical AND
As you can see, ternaries are wonderful for if/else
conditions. But what about simple if
conditions?
Let's look at another example. If isPro
(a boolean) is true, we are to display a trophy emoji. We are also to render the number of stars (if not zero). We could go about it like this.
const MyComponent = ({ name, isPro, stars }) => (
<div className="hello">
<div>
Hello {name}
{isPro ? '🏆' : null}
</div>
{stars ? <div>Stars:{'⭐️'.repeat(stars)}</div> : null}
</div>
);
But notice the "else" conditions return null
. This is because a ternary expects an else condition.
For simple if
conditions, we could use something a little more fitting: the logical AND operator (i.e. &&
). Here's the same code written using a logical AND.
const MyComponent = ({ name, isPro, stars }) => (
<div className="hello">
<div>
Hello {name}
{isPro && '🏆'}
</div>
{stars && <div>Stars:{'⭐️'.repeat(stars)}</div>}
</div>
);
Not too different, but notice how we eliminated the : null
(i.e. the "else" condition) at the end of each ternary. Everything should render just like it did before.
Here's a live demo of the code above.
Hey! What gives with John? There is a 0 when nothing should be rendered. That's the gotcha that I was referring to above. Here's why.
According to MDN, a Logical AND (i.e. &&):
expr1 && expr2;
Returns expr1
if it can be converted to false
; otherwise, returns expr2
. Thus, when used with Boolean values, &&
returns true
if both operands are true
; otherwise, returns false
.
OK, before you start pulling your hair out, let me break it down for you.
In our case, expr1
is the variable stars, which has a value of 0. Because zero is falsy, 0 is returned and rendered. See, that wasn't too bad.
I would write this simply.
If expr1
is falsy, returns expr1
, else returns expr2
.
So, when using a logical AND with non-boolean values, we must make the falsy value return something that React won't render. Say, like a value of false. There are a few ways that we can accomplish this. Let's try this instead.
{
!!stars && <div>{'⭐️'.repeat(stars)}</div>;
}
Notice the double bang operator (i.e. !!
) in front of stars
. (Well, actually there is no "double bang operator". We're just using the bang operator twice.)
The first bang operator will coerce the value of stars
into a boolean and then perform a NOT operation. If stars
is 0, then !stars
will produce true.
Then we perform a second NOT operation, so if stars
is 0, !!stars
would produce false. Exactly what we want.
If you're not a fan of !!, you can also force a boolean like this (which I find a little wordy).
{Boolean(stars) && (
Or... use a comparator that results in a boolean value which some might say is the best way. It's also more explicit.
{stars > 0 && (
Empty string values suffer the same issue as numbers. But because a rendered empty string is invisible, it's not a problem.
Be even more explicit and use a temp variable
For me, the best solution is one that not only scales to other variables in the future, but is explicit as to your intent. That would be to create a separate shouldRenderStars
variable. Then you are dealing with boolean values in your logical AND. This approach also eliminates the gotchas.
Here is the complete component that was shown above using this technique. I think it reads a bit better than the original. And because we're using boolean values, there's no risk of leaking a 0
into the DOM.
Using a temporary variable with a meaningful name also makes your code "self documenting".
Then, in the future, if the business rule is that you also need to be logged in, own a dog, and drink light beer, you could change how shouldRenderStars is computed, and what is returned would remain unchanged. You could also place this logic elsewhere where it's testable and keep the rendering explicit.
const shouldRenderStars =
stars > 0 && isLoggedIn && pet === 'dog' && beerPref === 'light`;
Conclusion
I'm of the opinion that you should make the best use of the language. And for JavaScript, this means using conditional ternary operators for if/else conditions and logical AND operators for simple if conditions.
Just remember that it's very important to always use a boolean to avoid the "0" rendering gotcha. A side benefit is that your code will be more explicit and easier to read.
There are, of course, some with opposing views. And while we could just retreat back to our safe comfy place where we use the ternary operator everywhere "just in case", you now possess the knowledge and power to go forth AND prosper.