Undefined: The Third Boolean Value

Avatar of Kushagra Gour
Kushagra Gour on

I wanted to implement a notification message in one of my projects, similar to what you’d see in Google Docs while a document is saving. In other words, a message shows up indicating that the document is saving every time a change is made. Then, once the changes are saved, the message becomes: “All changes saved in Drive.”

Let’s take a look at how we might do that using a boolean value, but actually covering three possible states. This definitely isn’t the only way to do this, and frankly, I’m not even sure if it’s the best way. Either way, it worked for me.

Here’s an example of that “Saving…” state:

Screenshot of the Google Docs header with a message that says the document is saving.
The “Saving” notification displays any time a change is made to the document.

…and the “Saved” state:

Screenshot of the the message reading All Changes Saved after saving has completed.
The “Saved” notification displays once a change or set of changes has completed.

Using a Boolean value for to define the state was my immediate reaction. I could have a variable called isSaving and use it to render a conditional string in my template, like so:

let isSaving;

…and in the template:

<span>{{ isSaving ? ‘Saving...’ : ‘All changes saved’ }}</span>

Now, whenever we start saving, we set the value to true and then set it to false whenever no save is in progress. Simple, right?

There is a problem here, though, and it’s a bit of a UX issue. The default message is rendered as “All changes saved.” When the user initially lands on the page, there is no saving taking place and we get the “Saved” message even though no save ever happened. I would prefer showing nothing until the first change triggers the first “Saving” message.

This calls for a third state in our variable: isSaving. Now the question becomes: do we change the value to a string variable as one of the three states? We could do that, but what if we could get the third state in our current boolean variable itself?

isSaving can take two values: true or false. But what is the value directly after we have declared it in the statement: let isSaving;? It’s undefined because the value of any variable is undefined when it’s declared, unless something is assigned to it. Great! We can use that initial undefined value to our advantage… but, this will require a slight change in how we write our condition in the template.

The ternary operator we are using evaluates to the second expression for anything that can’t be converted to true. The values undefined and false both are not true and, hence, resolve as false for the ternary operator. Even an if/else statement would work a similar way because else is evaluated for anything that isn’t true. But we want to differentiate between undefined and false . This is fixable by explicitly checking for false value, too, like so:

<span>
{{ isSaving === true ? 
  ‘Saving...’ : 
  (isSaving === false ? ‘All changes saved’: ‘’) 
}}
</span>

We are now strictly checking for true and false values. This made our ternary operator a little nested and difficult to read. If our template supports if/else statements, then we can refactor the template like this:

<span>
{% if isSaving === true %}
  Saving...
{% elseif isSaving === false %}
  All changes saved
{% endif %}
</span>

Aha! Nothing renders when the variable is neither true nor false — exactly what we want!