Skip to content

The Pitfalls of React Short-Circuit Evaluation

10 min read

A pedestrian traffic light, with don't walk hand and zero seconds remaining
Image credit: @theblowup

Short-circuit evaluation is a React pattern that's frequently used as a shorthand for conditionally rendering JSX based on the result of an expression. For example, you could choose to only render a child component if data that it depends on is truthy:

const Component = ({ input }) => {
return <div>{input && <ChildComponent input={input} />}</div>;
};
// if input is truthy -> `<div><ChildComponent /><div>`
// if input is falsy -> `<div></div>`
jsx

Many developers like using short-circuit evaluation because it's a bit more concise than an explicit conditional, like a ternary operator:

const Component = ({ input }) => {
return <div>{input ? <ChildComponent input={input} /> : null}</div>;
};
jsx

Although short-circuit evaluation is a valid and useful React pattern, there are a couple potential traps that React developers can unwittingly fall into when using it. This blog post will give a brief overview of short-circuit evaluation, why it works, what kind of issues you could face with it, and how to use this syntax safely.

What is it, and why does it work?

The first thing to note is that short-circuit evaluation isn't limited to React. It's a general feature of JavaScript that you can use anywhere to concisely choose which expression to evaluate based on their values.

You can create a short-circuit evaluation by using either && (logical AND operator) or || (logical OR operator). For example:

const varOne = true && 'A';
// varOne === 'A'
const varTwo = false || 'B';
// varTwo === 'B'
js

The result of the code above may be surprising to you, especially if you're used to seeing logical operators in if statements, like:

if (5 === 5 && true === true) {
// if 5 is 5 and true is true: do something
}
js

It would be natural to think that varOne and varTwo would both have values of true, since you're probably used to seeing logical operators like && and || used to return a boolean, in a conditional statement. However, this isn't technically how logical operators work. Logical operators actually return one of their operands, based on a fairly obscure set of rules.

The way the logical AND operator (&&) works is that it starts evaluating the operands, from left to right, and returns the value of the first operand if it can be converted to false. Otherwise it evaluates and returns the value of the second operand, regardless of its truthiness. So for example:

let varOne = null && 'Hello, world!';
// varOne === null;
// because the first operand can be converted to false
let varTwo = 'Hello, world!' && 0;
// varTwo === 0
// because the first operand can be converted to true
let varThree = 'Hello, world!' && 'Short-circuiting is fun!';
// varThree === 'Short-circuiting is fun!'
// because the first operand can be converted to true
js

The logical OR operator (||) works similarly: it evaluates the operands from left to right, and it returns the value of the first operand if it can be converted to true. Otherwise it evaluates and returns the value of the second operand. So:

let varOne = null || 'Hello, world!';
// varOne === 'Hello, world!'
// because the first operand is falsy
let varTwo = 'Hello, world!' || 0;
// varTwo === 'Hello, world!'
// because the first operand is truthy
let varThree = 'Hello, world!' || 'Short-circuiting is fun!';
// varThree === 'Hello, world!'
// because the first operand is truthy
js

Note: The reason these operators work in an if statement is that the if statement will evaluate whatever operand is returned, and convert it into either true or false. In other words, the logical operator produces a value, and the if statement does the work of converting that value into a boolean, if it isn't one already.

An important thing to note about short-circuiting evaluation is that it stops evaluating on the first operand if it can, and the second expression may never be evaluated. Take for example:

console.log(false && someDangerousCode());
console.log(true || someDangerousCode());
// in both cases, someDangerousCode() will NOT run,
// because the code stops on the first operand
// BUT:
console.log(true && someDangerousCode());
console.log(false || someDangerousCode());
// both of these functions will run
js

Use in React

Let's look at an example of how this all works in React:

const ProfileName = ({ name }) => {
return <span>{name}</span>;
};
const ProfilePage = ({ user }) => {
return (
<div>
{/* ... the rest of the profile page's contents */}
{user?.name && <ProfileName name={user.name} />}
</div>
);
};
jsx

We don't want to accidentally render bad data, so we only want to render the <ProfileName /> component if there is a valid username (e.g., if user is not undefined, and it has a truthy value for its name property). So if user.name is truthy, then <ProfilePage /> will render <ProfileName /> and pass it user.name.

You may be wondering what happens if user.name is not truthy (e.g., it is undefined). Recall that for && short-circuit evaluation, if the first operand is falsy, it returns the first operand, and stops executing. So in our example, the whole expression would result in undefined, and the component will never be rendered. This is generally ok, because if you try to render values like false, undefined, or null in React, nothing will actually be rendered to the DOM. So here we can safely ignore the fact that the expression is actually returning a value like undefined.

Potential issues

However, consider the following component, which is code that I've adapted from a weather app project that I built:

export const WeatherDetails = ({ weather }) => {
const {
humidity,
//...
} = weather;
return (
<table>
<tbody>
{/* ... other table entries */}
{humidity && (
<tr>
<th>Humidity</th>
<td>{humidity}%</td>
</tr>
)}
{/* ... other table entries */}
</tbody>
</table>
);
};
jsx

For some background: the component accepts a weather object as props, which contains details about the current day's weather. The component then extracts the data and renders it in an HTML table.

The problem is that I get my weather data from an API that doesn't always return a consistent response. For example, if there are no recorded wind gusts in the forecast, the API will send a response that doesn't include windGusts at all (instead of something like weather.windGusts: 0). So in other words, weather.windGusts could be a number or undefined, or maybe even null, depending entirely on the API.

Because the API is (frankly) not great about documenting which weather properties may or may not be included in the response, I need to check all of the entries in the API response, to be safe. If I didn't check the data before trying to render it, bad things could happen. I might pass my data to functions that expect one data type (like a number), when the actual value is undefined (potentially causing a TypeError). Or I might render empty data, like:

<tr>
<th>Wind Gusts</th>
<td>mph</td>
</tr>
html

But as it stands now, there's actually a big problem with my component. When weather.humidity is present in the data, it should be a number, and the number 0 is falsy. So what would happen if weather.humidity was 0?

You might think that, just like in the examples of short-circuiting evaluation in the previous section, React would skip over it and the following code would render nothing:

{
humidity && (
<tr>
<th>Humidity</th>
<td>{humidity}%</td>
</tr>
);
}
jsx

What actually happens is JavaScript evaluates the && according to the rules mentioned above: it sees that the left-hand operand is falsy (0), and returns it. React will see the 0 in our JSX, and assume that we're actually trying to render a 0 to the DOM (after all, we render numbers all the time). So it does just that. React renders a rogue 0 inside our table, and we'll probably get a nice console warning about our page containing invalid HTML. Obviously, this isn't what we were going for.

It turns out that for React, not all falsy values are created equal. If you use short-circuit evaluation with undefined, null, false, or '', nothing will be rendered to the DOM. However, numbers are a different story. If you're trying to short-circuit with a number, and that number ends up being 0, then a single 0 will be rendered to the DOM.

The same thing actually happens with NaN. So you also need to be careful about using certain functions or operations that could return NaN, particularly when using data from an API:

const BrokenComponent = ({ numFromApi }) => {
// imagine that numFromApi is 'five':
const numToRender = parseInt(numFromApi);
return <div>{numFromApi && numFromApi}</div>;
};
// renders <div>NaN</div>
jsx

The consequence of all of this is that if you're not being careful with your code, you could accidentally end up rendering rogue 0s and NaNs to your page or app. And because this likely won't produce any actual errors, it could be a long time before you even notice the problem.

Possible solutions

So what should we do? Well, the issue really only applies to numbers, since the only falsy values that will render are 0 and NaN. So any time you use short-circuit evaluations with a variable or expression that could result in a number or NaN, you should take precautions. One option would be to just use a ternary operator if num could be a number:

return <div>{num ? <SomeComponent /> : null}</div>;
jsx

This is a pretty straightforward fix, and it's usually what I do. But you could still use short-circuit evaluation if you explicitly convert the value to a boolean:

return <div>{!!num && <SomeComponent />}</div>;
jsx

But there might also be cases where you actually want to render something if num === 0. For example, in our weather app, 0% humidity is actually useful weather data to give to our users, so we really just want to make sure it's a number, and not undefined or null:

return (
<table>
<tbody>
{/* ... other table entries */}
{typeof humidity === 'number' && (
<tr>
<th>Humidity</th>
<td>{humidity}%</td>
</tr>
)}
{/* ... other table entries */}
</tbody>
</table>
);
jsx

Conclusion

After all this, you might be asking, "why should I use short-circuit evaluation at all, when I could just use the ternary operator?" And sure, if you wanted to be safe, you could stop using short-circuit evaluation entirely, and just rely on ternary operators to conditionally render JSX. Your code wouldn't be that much longer, and nothing bad would happen. That would be a totally valid choice to make!

But I'm a big proponent of not throwing the baby out with the bathwater. I think short-circuit evaluation is a great declarative syntax for conditionally rendering components in React. It's just that, like a lot of things with JavaScript, the trick is understanding the nuances of how it works behind the scenes, so you can learn how to use it safely.