Stop using boolean states

Stop using boolean states

They are more dangerous than you think.

Introduction

We often use boolean states to keep track of whether something is loading.

Usually, it works as expected.

But maybe you should stop doing it. It's almost always better to have a status.

Let's dive into an example to clarify the dangers of using boolean states. I will stick to plain JavaScript for demonstration purposes, but it applies to any frameworks you'd be using.

The problematic approach

To make things a bit clear, I'm taking the extreme approach. Let's look at a piece of code for our registration process and examine how boolean states could become problematic:

let isValidating = false;
let isCreatingAccount = false;
let isSendingEmail = false;
let isRegistrationComplete = false;
let hasValidationError = false;
let hasCreationError = false;
let hasEmailError = false;

function registerUser(userData) {
  isValidating = true;

  // Step 1: Validate user data
  validate(userData, (validationResult) => {
    isValidating = false;
    if (!validationResult.ok) {
      hasValidationError = true;
      return;
    }

    // Step 2: Create account
    isCreatingAccount = true;
    createAccount(userData, (creationResult) => {
      isCreatingAccount = false;
      if (!creationResult.ok) {
        hasCreationError = true;
        return;
      }

      // Step 3: Send welcome email
      isSendingEmail = true;
      sendWelcomeEmail(userData, (emailResult) => {
        isSendingEmail = false;
        if (!emailResult.ok) {
          hasEmailError = true;
          return;
        }

        // Step 4: Registration complete
        isRegistrationComplete = true;
      });
    });
  });
}

How can this become problematic?

  1. Complex conditional logic: At each step, you need to check multiple flags to understand the current state.

  2. Hard to maintain: If a new step or error condition is added to the process, you need to add more flags and update the logic in several places.

  3. Risk of contradictory states: There's a risk that multiple flags could be true at the same time due to bugs, leading to states that don't make sense.

The right approach

Let's take a look at how this would be different when using a status.

let registrationStatus = 'idle'; // Can be 'idle', 'validating', 'creatingAccount', 'sendingEmail', 'complete', 'error'

function registerUser(userData) {
  updateRegistrationStatus('validating');

  // Step 1: Validate user data
  validate(userData, (validationResult) => {
    if (!validationResult.ok) {
      updateRegistrationStatus('error', validationResult.error);
      return;
    }

    updateRegistrationStatus('creatingAccount');
    // Step 2: Create account
    createAccount(userData, (creationResult) => {
      if (!creationResult.ok) {
        updateRegistrationStatus('error', creationResult.error);
        return;
      }

      updateRegistrationStatus('sendingEmail');
      // Step 3: Send welcome email
      sendWelcomeEmail(userData, (emailResult) => {
        if (!emailResult.ok) {
          updateRegistrationStatus('error', emailResult.error);
          return;
        }

        // Step 4: Registration complete
        updateRegistrationStatus('complete');
      });
    });
  });
}

function updateRegistrationStatus(newStatus, error) {
  registrationStatus = newStatus;
  if (error) {
    console.error(`Registration error: ${error}`);
  }
  console.log(`Registration status: ${registrationStatus}`);
}

This is so much easier to maintain, change and use.

We can only be in one state at a time.

You might wonder what could happen if someone mistypes a status?

Then use a typed language!

If that isn't possible, use a constant to be consistent with the naming of the different statuses.

Conclusion

Stick to using statuses, not boolean states.