JS Gotchas with logical operators ||, &&, and ??

Javascript’s logical OR || and logical AND && operators are useful in contexts other than creating if, else, while etc. statements, but there are some edge cases that that I see many devs (myself included) getting caught out by.

This article explores common pitfalls, and provides recommendations for:

  1. using ?? instead of || to define values with a default fallback, and
  2. using && to conditionally render JSX components only when required props are defined.

A table of basic results for ||, &&, and ?? is also provided as a quick reference.

tl;dr

  1. Use the nullish coalescing (??) operator, not the logical OR (||) operator to initialize variables with a default value as a fallback (i.e const value = input ?? DEFAULT_VALUE).
  2. To conditionally render a component in JSX only when it’s prop(s) are defined, { prop && <Component prop={prop} /> } is very convenient, but exercise caution when typeof prop === 'string', and when typeof prop === 'number', use { prop !== undefined && prop !== null && <Component prop={prop} /> } instead.
  3. Basic results for ||, &&, and ?? are tabulated as a quick reference:
┌──────────────────────┬───────────┬────────┬────────┐
        [a, b]          a && b    a || b  a ?? b 
├──────────────────────┼───────────┼────────┼────────┤
   [ null, 'foo' ]       null     'foo'   'foo'  
 [ undefined, 'foo' ]  undefined  'foo'   'foo'  
    [ '', 'foo' ]         ''      'foo'     ''   
     [ 0, 'foo' ]          0      'foo'     0    
    [ NaN, 'foo' ]        NaN     'foo'    NaN   
└──────────────────────┴───────────┴────────┴────────┘

The Gotchas

Gotcha #1: Using || instead of ?? to define values with a default fallback

The logical OR (||) operator is part of the syntax you probably learned on your first day of writing javascript, and you would already be comfortable using it to write conditional if, else, while etc. statements.

Then one day, you discover it can also be used to initialize variables to a default value:

let userInput;
const projectName = userInput || "untitled project";
console.log(projectName);
// returns "untitled project"

Nice! You have seen some people use the nullish coalescing operator (??), which seems to do the same thing (at least for this example), so why not just stick with the more familiar || syntax, and start using this to initialize variables everywhere? 🤔

MDN provides the below warning:

Note: If you use this operator to provide a default value to some variable, be aware that any falsy value will not be used. If you only need to filter out null or undefined, consider using the nullish coalescing operator.

Daniyal Hamid sums up the difference:

The OR operator returns the value on the right if the expression on the left is falsy, while the nullish coalescing operator returns the value on the right if expression on the left is a nullish value (i.e. null or undefined);

Let’s test a few examples to see how both operators compare:

const logiOr = (a, b) => a || b; // logical OR operator
const nullCo = (a, b) => a ?? b; // nullish coalescing operator

const testInputs = [
  [null, "foo"],
  [undefined, "foo"],
  ["", "foo"],
  [0, "foo"],
  [NaN, "foo"],
];

console.table(
  testInputs.map(([a, b]) => {
    return {
      inputs: [a, b],
      logiOr: logiOr(a, b),
      nullCo: nullCo(a, b),
      sameResult: logiOr(a, b) === nullCo(a, b),
    };
  })
);

// output:
┌──────────────────────┬────────┬────────┬────────────┐
        inputs         logiOr  nullCo  sameResult 
│──────────────────────┼────────┼────────┼────────────┤
   [ null, 'foo' ]     'foo'   'foo'      true    
 [ undefined, 'foo' ]  'foo'   'foo'      true    
    [ '', 'foo' ]      'foo'     ''      false    //<-gotcha!
     [ 0, 'foo' ]      'foo'     0       false    //<-gotcha!
    [ NaN, 'foo' ]     'foo'    NaN      false    //<-gotcha!
└──────────────────────┴────────┴────────┴────────────┘

We can see that while '', 0, and NaN are falsy, they are not nullish. Overlooking this difference would introduce bugs where a falsy value is actually a valid input:

// calculating golf scores
const DEFAULT_HANDICAP = 3;
const getScore = (points: number, handicap?: number) =>
  points - (handicap || DEFAULT_HANDICAP);
getScore(10, 0); // returns `7`, instead of the expected `10`

// adding text to a label (or maybe a sign in a game)
const DEFAULT_TEXT = "click to edit text";
const labelText = (input: string) => input || DEFAULT_TEXT;
// user tries to create a blank label / sign
getText(""); // returns "click to edit text", instead of the expected ""

Recommendation

Of course there are cases where it is ok to use the || operator to fall back to a default value, but in my opinion the ?? operator should still be used, with any additional special handling for falsy values being explicitly defined, resulting in clear, readable code that is less likely to be broken by the next developer who may reasonably lack a complete knowledge of expected inputs or overlook the subtle implications of previous syntax choices.

Gotcha #2: Using && to conditionally render components in JSX without considering all falsy values

The logical AND (&&) is another operator everyone is comfortable using to create conditional statements, but it also has more general uses. MDN explains:

the [&&] operator returns the value of the first falsy operand encountered when evaluating from left to right, or the value of the last operand if they are all truthy.

A common use is to conditionally render React components / JSX elements – in the below example, the <ErrorDetails/> component is only rendered when the errorText prop is provided:

const ErrorMessage = (errorText?: string) => {
  return (
    <>
      <div>An error occurred</div>
      {errorText && <ErrorDetails text={errorText} />}
    </>
  );
};

This is a very convenient and concise way to write JSX. If errorText === undefined or errorText === null, the left-hand operator is a falsy value, so undefined or null is returned instead of the <ErrorDetails /> component, which the compiler then ignores when converting the JSX to HTML. Woo! I love JSX 🥳

But don’t party to hard just yet, because there are a a coupe of edge cases to be aware of…

The component won’t render if errorText === ''

If you read the previous gotcha, you are already step ahead and know that if errorText === '', the component will not render, and the empty string returned instead of the ErrorDetails /> results in nothing being rendered.

Mysterious 0s and NaNs appearing in the UI

In addition to the effects of '', 0, and NaN being falsy, we additionally see some interesting behavior when the left-hand operator is NaN or 0. The below example essentially uses the below JSX for conditional rendering:

return <>
  {value && <div>Value `{value}` is TRUTHY</div>}
  {!value && <div>Value `{value}` is FALSY</div>;}
</>

At a quick glance, it looks like only one element should ever render, which is technically true, but look what appears when you set value === NaN or value === 0:

See the Pen React Template by chrispalmo (@chrispalmo) on CodePen.


Including results for a new function const logAnd = (a, b) => a && b for the tests above helps clarify why this occurs:

┌──────────────────────┬───────────┬────────┬────────┐
        inputs          logAnd    condOr  nullCo 
├──────────────────────┼───────────┼────────┼────────┤
   [ null, 'foo' ]       null     'foo'   'foo'  
 [ undefined, 'foo' ]  undefined  'foo'   'foo'  
    [ '', 'foo' ]         ''      'foo'     ''   
     [ 0, 'foo' ]          0      'foo'     0    
    [ NaN, 'foo' ]        NaN     'foo'    NaN   
└──────────────────────┴───────────┴────────┴────────┘

Just as '' && <Component /> returns '', the falsey left-hand operator is also returned for NaN && <Component /> and 0 && <Component />. The problem is that 0 and NaN are both 'number' types, which are included in the HTML output as visible elements.

Recommendation

Using prop && <Component prop={prop}/> is convenient, concise, and works most of the time, but can cause surprises if used without caution. When typeof prop === 'number', a robust way to conditionally render the component without sacrificing too much brevity would be:

prop !== undefined && prop !== null && <Component prop={prop} />;

Questions?

Do you have any questions or feedback? Feel free to leave a comment below, or to reach out to me directly.