challenges

JSX and the && Operator (Solution)

The goal: hide the number of correct answers if the student hasnt answered any questions yet.

Why doesn’t && work as expected here?

Code with error

function QuizStatus({ numAnswered, numCorrect }) {
  return (
    <div>
      <p>Total answered: {numAnswered}</p>
      {numAnswered && 
        <p>Total correct: {numCorrect}</p>}
    </div>
  )
}

Corrected code

function QuizStatus({ numAnswered, numCorrect }) {
  return (
    <div>
      <p>Total answered: {numAnswered}</p>
      {numAnswered > 0 && 
        <p>Total correct: {numCorrect}</p>}
    </div>
  )
}

Explanation

&& (the logical AND operator) can be a convenient shortcut in JavaScript. But what exactly is the difference between the two code snippets above? To answer that, it helps to know what && does.

Logical AND in C

Allow me to wax nostalgic for a moment. I first encountered && in C , where the operator’s sole purpose is to logically combine multiple expressions.

Take the expression a && b. I’ll call a and b “sub-expressions” here. To get the value of a && b, you need to look at each sub-expression individually. If both a and b are true, then a && b evaluates to true; otherwise a && b evaluates to false. As a kind of shorthand, I mentally read the statement a && b as “a and b are (both) true.”

A couple examples:

bool five_is_tiny = 5 < 20 && 5 < 25;
/* true */
 
bool ninety_is_tiny = 90 < 20 && 90 < 25;
/* false */

An interesting side-effect of this: if the first sub-expression evaluates to false, C won’t even look at the second sub-expression. All of the sub-expressions need to be true in order for the expression with && to return true — so if the first sub-expression is false, then the whole expression evaluates to false. No need to process the rest of the expression.

Surprisingly, this code runs without error:

bool never_divide_by_zero = 90 < 20 && 6/0 < 20;

Since 90 < 20 is false, C can set never_divide_by_zero to false without evaluating the second expression (6/0 < 20). The illegal division by zero never occurs!

Logical AND in JavaScript

JavaScript takes this one step further by returning one of the expressions instead of a boolean. Like C, JavaScript evaluates the expressions separated by the && operator from left to right. Also like C, JavaScript will stop at the first falsy expression, since no more evaluation is needed to know the whole thing is false.

However, instead of returning false at that point, JavaScript returns the value of the first falsy expression. (If none of the expressions turns out to be falsy, JavaScript will return the last expression.) Consider this function:

function getErrorString(errorString) {
  return errorString 
    && `SOMETHING WENT WRONG!!! ${errorString}`
}

getErrorString returns the first falsy value it finds in the logical AND of the two sub-expressions in the return statement. Say getErrorString was passed the empty string ("") as the argument. In this case, errorString would evaluate to falsy and be returned.

If errorString is truthy , though, then the function will return the second sub-expression: the errorString prefixed with a less-than-calm introduction.

Logical AND in JSX

This brings us back to the original code snippet with the error. It’s pretty common to see elements conditionally displayed in JSX using the && operator:

{ 
  someExpression && 
  (
    <div>
      display this if someExpression is true!
    </div>
  )
}

If someExpression is truthy, then the above will evaluate to the <div> (that is, the last truthy expression in the logical AND statement). However, if someExpression is falsy, then the statement will stop evaluation and return someExpression, equivalent to this:

{ someExpression }

This works as expected if someExpression results in empty JSX. For example, if someExpression is null, JSX won’t create an element from that. Same with values of undefined or false for someExpression.

However, some falsy expressions do get converted into JSX elements. Recall the original Code Check snippet:

{numAnswered && 
  <p>Total correct: {numCorrect}</p>}

Suppose numAnswered is 0. The value 0 is falsy, so numAnswered is returned without considering the “Total correct” element. So far so good… except that 0 is perfectly legitimate JSX. So the above expression becomes 0 as far as JSX is concerned, and JSX happily displays the 0 on the page. (Full disclosure: I have not asked JSX, so I’m not entirely sure how it feels about displaying 0 on the page.)

To get make sure your code doesn’t fall into this trap, it’s a good idea to use a boolean sub-expression before the && (something you know will evaluate to true or false ). In the corrected snippet above, I used numAnswered > 0. You could also use a ternary to avoid the “0 is both falsy and legitimate JSX” issue.

The moral of the story

The next time you see a stray 0 on a web page (I see them from time to time!), it’s a pretty good guess that someone misapplied the && operator.

Further reading