Optional Chaining is a newer JavaScript feature, introduced in ES2020. Since I discovered it, I've been using it quite a lot, because it provides a simple, clean way of dealing with a common issue: object properties which could either be present or missing.
Below is an object with several layers of nested properties. In it there are cats, but no dogs, and if you console.log dogs
you get undefined
.
let animals = {
mammals: {
cats: {
male: 3,
female: 3,
}
}
};
console.log(animals.mammals.cats); // => {male: 3, female: 3}
console.log(animals.mammals.dogs); // => undefined
Let's see what happens with conditional statements for cats
and dogs
:
if (animals.mammals.cats.male > 0) {
console.log(`There are male cats.`);
} else {
console.log("There are no male cats.");
}
// => There are male cats.
if (animals.mammals.dogs.male > 0) {
console.log(`There are male dogs.`);
} else {
console.log("There are no male dogs.");
}
// => Uncaught TypeError: Cannot read properties of undefined (reading 'male')
Trying to access a property of an undefined property doesn't return undefined
again; it throws an error and halts the execution of the entire script. In previous versions of JavaScript, we would have to add another conditional layer to avoid this:
if (animals.mammals.dogs) {
if (animals.mammals.dogs.male > 0) {
console.log(`There are male dogs.`);
}
} else {
console.log("There are no male dogs.");
}
// => There are no male dogs.
With optional chaining, we can retain the simpler syntax, with the addition of one question mark after the property in question:
if (animals.mammals.dogs?.male > 0) {
console.log(`There are male dogs.`);
} else {
console.log("There are no male dogs.");
}
// => There are no male dogs.
Now, if the property is not found, the evaluation stops at the question mark, and undefined
is returned. The question mark can be used multiple times, for different levels of the same object: animals.mammals?.dogs?.male
.
Optional chaining also works when selecting an item from an array nested inside an object, with the following syntax: films.horror?.[0]
.
One excellent use for conditional chaining is conditional rendering in React. In the example below, the state object has three nested properties which may or may not be present. Previously, this would have required as many lines of code to check for each one in succession before preceding to map
:
return(
{ state.entry &&
state.entry.headword &&
state.entry.headword.morphs.notes &&
state.entry.headword.morphs.notes.map((a,i) => (
<Note key={i} />
))
}
);
With conditional chaining we can reduce that to one line. If any property is missing, rather than throwing an error, the component will simply not render.
return(
{ state.entry?.headword?.morphs.notes?.map((a,i) => (
<Note key={i} />
))
}
);