It often happens that, as a React Developer, you’re working with big data objects containing a lot of nested properties, especially those coming from an external API.

Because of that, it’s not always possible for us to tell the exact shape of the object at runtime, as sometimes, due to unforeseeable issues, our objects come out to be something else than what we were expecting them to be.

In order to avoid any issues that might appear due to this type of situation, we’ll look at one feature introduced during ES6 in JavaScript called Optional Chaining.

What is Optional Chaining?

As its name denotes, it tries to chain a property retrieval only if it makes sense.

That means that, for example, if we were to try to grab a property from an object whose value is either null or undefined and then try to grab a property of that initially grabbed property (Object.property1.property2), it will consider that operation invalid and will return us undefined by default without throwing any issues that would otherwise cause our application to break.

That might sound a bit confusing, so let’s try going over an example in order to get a better idea of what Conditional Chaining actually does.

Optional Chaining Example

Let’s first define an user object with some nested properties that we’ll try to test certain retrieval processes upon:

  const user = {
    personalDetails: {
      name: 'Tommy',
      surname: 'Smith',
      age: 21
    },
    subscriptionDetails: {
      type: 'premium',
      price: 12.99
    }
  }

As you can see, our user object has 2 levels of nesting.

We’re going to be using this object in App.js to display the data entry points of our user:

function App({ user }) {
  return (
    <div style={{ margin: '50px' }}>
      <h1>Current user:</h1>
      <h2>Name: {user.personalDetails.name}</h2>
      <h2>Surname: {user.personalDetails.surname}</h2>
      <h2>Age: {user.personalDetails.age}</h2>
      <h2>Subscription type: {user.subscriptionDetails.type}</h2>
      <h2>Subscription price: {user.subscriptionDetails.price}</h2>
    </div>
  );
}

export default App;

That will generate the following screen rendering:

Let’s see what would happen if our server for some mysterious reason provided us with an incomplete user object:

const user = {
  personalDetails: {
    name: 'Tommy',
    surname: 'Smith',
    age: 21
  }
}

Our React application is not aware that the server sent a different version of the user object rather than what we were going to be working with. As such, it will try to access the nested properties of type and price which will result in a TypeError being thrown as it does in the screenshot below:

Object Property Retrieval Faulty Evaluation

It’s a bit annoying, isn’t it?

Luckily for us, in ES6, there was the introduction of the Optional Chaining feature which is a perfect fit for our very scenario. It uses the ?. syntax to access the properties on objects rather than the regular dot ( . ) notation.

It also works for the square brackets notation and functions invocations: ?.["property_name"]?. and ?.(function_argument) respectively.

Let’s now rewrite our code to account for the addition of this feature:

function App({ user }) {
  return (
    <div style={{ margin: '50px' }}>
      <h1>Current user:</h1>
      <h2>Name: {user.personalDetails.name}</h2>
      <h2>Surname: {user.personalDetails.surname}</h2>
      <h2>Age: {user.personalDetails.age}</h2>
      <h2>Subscription type: {user?.subscriptionDetails?.type}</h2>
      <h2>Subscription price: {user?.subscriptionDetails?.price}</h2>
    </div>
  );
}

export default App;

We’ve changed the last two h2 elements to use the new syntax.

If you were to check the application you would then see that the errors are no longer being shown.

However, it isn’t the perfect solution just yet. We can see that despite the application working, the properties have not been rendered on the screen, which is due to the fact that they’ve been evaluated to be undefined.

Optional Chaining Without Fallback
Optional Chaining Without Fallback

The issue can be fixed by accounting for any falsy evaluations and providing a fallback value/component to be rendered instead:

function App({ user }) {
  return (
    <div style={{ margin: '50px' }}>
      <h1>Current user:</h1>
      <h2>Name: {user.personalDetails.name}</h2>
      <h2>Surname: {user.personalDetails.surname}</h2>
      <h2>Age: {user.personalDetails.age}</h2>
      <h2>Subscription type: {user?.subscriptionDetails?.type || 'Type not found'}</h2>
      <h2>Subscription price: {user?.subscriptionDetails?.price || 'Price not found'}</h2>
    </div>
  );
}

export default App;

If we were to now check the application then we would see that it looks slightly better. You can come up with any fallback message or UI component that you believe will do the job better than we’ve done here (It obviously shouldn’t be hard).

Optional Chaining & Fallback
Optional Chaining & Fallback

Summary

Hopefully, you’ve enjoyed the read and you now have a better idea of what Optional Chaining is, what it does, how it can be used, and more importantly when it should be used.

Since the concept is a rather simple one, we’ve gone through a sole example demonstrating the usefulness of the feature.

If you still aren’t convinced and would rather have errors take place in your application, be sure to check out this article which will teach you how you can handle any thrown errors in React gracefully.

See you on the next one. Cheers!

👋 Hey, I'm Vlad Mihet
I'm Vlad Mihet, a blogger & Full-Stack Engineer who loves teaching others and helping small businesses develop and improve their technical solutions & digital presence.

💬 Leave a comment

Your email address will not be published. Required fields are marked *

We will never share your email with anyone else.