DEV Community

Paige Niedringhaus
Paige Niedringhaus

Posted on • Originally published at paigeniedringhaus.com on

Use ES6 To Destructure Deeply Nested Objects in JavaScript

Sparta meme: "This is madness? This is ES6 destructuring"

Introduction

In case this is your first time reading one of my posts, thanks for taking the time, I am a full stack software developer with a fondness for JavaScript, and the React framework, in particular.

For just over a year, in my free time after work, I’ve been teaching myself React with the help of online documentation, articles, tutorials and building (and breaking) side projects.

With all these sources of knowledge and inspiration, I managed to build a user registration application that I was pretty proud of. It had a React front end, an Express/Node.js server back end, a MySQL database, it used Passport.js and JSON Web Tokens to handle authentication, the Sequelize ORM to perform CRUD operations on the database, a password reset feature through Nodemailer, Swagger endpoint testing on the REST API endpoints, a docker-compose.yml file to spin up all three pieces of the application simultaneously in a virtualized container environment, etc., etc. Have you had enough technology buzzwords thrown at you, yet? 😫

Anchor Man: I'm kind of a big deal

Yes, I threw out all those buzzwords, no, I‘m really not that big of a deal. 😉

Please understand, this project was by no means achieved in a day, it started as a very basic idea and mushroomed in scope as I made forward progress and got more ambitious to see how far I could push it. But by the time I’d gotten to that point, I was feeling pretty proud of myself. I’d made a cool, useful little side project — completely built in JavaScript.

Then I introduced Airbnb’s ESLint configuration into the mix, and was humbled as the VS Code browser lit up left and right with red squiggles of disapproval. 😞 Instead of quitting though, I persevered in learning the best practices of modern JavaScript/ES6 development (according to ESLint and Airbnb), fixed the majority of my errors and became a better developer along the way.

There was one error, however, which eluded me. The deeply nested, destructured object. How, pray tell, do you destructure a prop` object that’s several levels deep in JavaScript to ESLint’s satisfaction?

Today, I will show you how to use ES6 to destructure nested objects, my friends, AND what’s more, prevent the dreaded undefined error if an object is missing that property entirely.

Let’s get to it.


The issue: ES6 object destructuring & deeply nested objects

To give you some insight into what I’m describing, let me back up just a minute and go over what object destructuring in JavaScript is, and why it’s a little daunting once your objects get complex.

Object Destructuring in a Nutshell

As per usual, MDN’s documentation sums up JavaScript destructuring nicely:

The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables. — MDN Docs, Destructuring Assignment

This is a feature introduced in ES6, and what it means, in practice, is that your code can go from looking like this:

Plain old JavaScript object

js
const myObject = {
a : "foo",
b : false,
c : 11
};

Traditional JS object property access example (dot notation and bracket notation)

With the object’s properties being accessed through dot notation or bracket notation like this:

javascript
console.log(myObject.a); // "foo"
console.log(myObject[b]); // false
console.log(myObject.c); // 11

ES6 object property access example (destructured)

To being accessed like this:

javascript
const { a, b, c } = myObject;
console.log(a); // "foo"
console.log(b); // false
console.log(c); // 11

That’s pretty cool, right? It’s shorter, it’s much more concise to read, and it makes pretty good sense logically.

There’s a lot more cool things that can be done with both arrays and object and destructuring, and I’d recommend you read the docs for more examples.

Ok, so now that ES6’s destructuring has been defined, let’s look at my issue, which seemed a lot less straightforward with how to approach it (at least, at first pass, to me).

A deeply nested object

Below is an example of the object I was working with in my React application.

javascript
this.props.match.params.username;
// what a mouthful! no one wants to write this anymore

This was my nested object, written in traditional JavaScript dot notation syntax.

To give a little more context around it, this object was being taken from the URL string and passed into the HTTP call from the browser’s client to the server side to identify (and validate) the username in the database. This syntax is courtesy of the react-router-dom npm package and React’s props.

Regardless of where the object’s syntax and structure originated from, it’s complicated. It’s technically nested 4+ layers deep in this object to get to the useful piece of information, the username.

And unlike, const {a} = myObject;, trying to figure out how to dive several levels deep into an object was a little befuddling.

The ESLint errors: merciless as always

But did ESLint care?

Honey badger meme: ESLint don't care

I never imagined I’d have reason to use a honey badger meme in a blog post, and yet, here it is.

In the immortal words of honey badger: “nope”.

As soon as ESLint saw this type of syntax in my code, sirens went off and the ESLint error showed up: Must use destructuring props assignment eslint(react/destructuring-assignment).

ESLint destructuring props error message in VS Code

Thanks for being so helpful, ESLint, this is an extremely useful warning message. 🙄

I visited the ESLint documentation, made a few half-hearted attempts to figure out how to pull this convoluted mess of objects apart, but eventually gave up and resorted to the old * eslint-disable react/destructuring-assignment */ import at the top of my files instead of fixing it properly. And I moved on with my life. 😬

Then, a while later, another developer pointed me towards an article that showed me the light, while we were trying to decide how best to guard our code against throwing errors if environment variables weren’t specified during local development.

After reading it, things made much more sense. Here is what I learned.

How to fix the ESLint "Destructuring Props" error

Apparently, I was trying to make the solve for object destructuring more complicated than it needed to be. Here’s a breakdown (or build up) to my object, with destructuring along the way.

One JavaScript object

javascript
const myObject = {
props: "Hello world"
};

The destructured version becomes:

`javascript
const {
props
} = myObject;

console.log(props); // prints: "Hello world"
`

Right, that seems logical, access the property’s value in the object just by wrapping that property in curly braces.

Next, up...

One JS object within another object

For an object inside another object, like:

javascript
const myObject = {
props: {
match : "Some value"
}
};

The destructured version becomes:

`javascript
const {
props : {
match
},
} = myObject;

console.log(match); // prints: "Some value"
`

All right, so to access property values two levels deep, first wrap the original property inside the top level object (in this case props) in curly braces, then, inside that object, wrap the inner object, match, in another set of curly braces.

Ok, following so far...

One JS object within two more objects

For a slightly more complex object, like:

javascript
const myObject = {
props: {
match: {
params: "A new value"
}
}
};

The new destructured version becomes:

`javascript
const {
props: {
match: {
params
},
},
} = myObject;

console.log(params); // prints: "A new value"
`

Once more, stepping through each tier: props gets wrapped in the first set of curly braces, match, the object contained within props is wrapped in the second set of curly braces, and finally, the third object params (with the value I want to access) is wrapped in the third set of curly braces.

The pattern is starting to make sense when deconstructed object by object.

One JS object within three. More. Objects

So, for an even more complex object, like this:

javascript
const myObject = {
props: {
match: {
params: {
username: "Paige"
}
}
}
};

The new destructured version becomes:

`javascript
const {
props: {
match: {
params: {
username
},
},
},
} = myObject;

console.log(username); // prints: "Paige"
`

The same rules and pattern as above still apply, just keep going one level further with the curly braces to reach the object properties you want to read.

To fix the ESLint error my React application was throwing: Must use destructuring props assignment eslint(react/destructuring-assignment), this is the solution I came up with.

javascript
const {
match: {
params: { username },
},
} = this.props;

Since ESLint only wanted object destructuring from the props object level and onwards, I was able to make my object slightly smaller than a truly, entirely destructured object. Access the match object inside this.props, access the params object inside match, and finally, access the value attached to username for the info I was seeking.

Broken down into steps like that, everything started making a lot more sense.

Nice! But then I thought of an edge case, what if, for some reason, one of these properties was missing? Ah, there’s a way to safeguard against undefined errors too within destructured objects.

How to prevent undefined errors for missing properties: default values

I can’t tell you the number of times I’ve thrown errors in local development because a property on an object I was trying to access was undefined, meaning the property (and its value) didn’t exist on that object. Not as an empty string, nor as an empty array or even a 0. It just flat out didn’t exist.

TypeError: Cannot read property ‘params’ of undefined. It’s so annoying — especially when it causes React to throw a giant error in the browser. 😠

But there’s a way around this with object default values. Another nice feature of ES6, in the case that the value unpacked from the object is undefined, is that that variable can be assigned a default value.

That’s a whole other blog post, but what this means for me, in the case of a missing property on a destructured object, is that instead of throwing a TypeError, I could provide a fallback value in the form of a string or other value, and I’d receive that value, which I could specifically look for and then move on from.

Here’s an example with an object with just one property.

Plain old JavaScript object

javascript
const myObject = {
props: "I only have props, nothing else"
};

And the destructured version:

`javascript
const { props } = myObject;

console.log(props); // prints: "I only have props, nothing else"
`

If I tried to access an undefined property within a destructured object, normally:

ES6 destructured object with no default props

`javascript
const {
props: {
match
}
} = myObject

console.log(match); // throws error: TypeError: Cannot read property "match" of undefined
`

But, if I set a default value to fall back on if that property doesn’t exist, within that destructured object, like so:

ES6 destructured object with default props

javascript
const {
props: { match } = { match: "No match"}
} = myObject
console.log(match); // prints: "No match"

And with an empty object, an empty array, a default string or integer, or a host of other things besides the dreaded undefined error, I can set up error handling in the DOM to display some sort of message to the user so they know what’s going on and how to fix the situation —a much better experience than an obvious React error screen with a cryptic message that something’s gone wrong.

So, here’s a rewritten version of the destructured object my app needed with a username from the URL string. If the username’s not present in the URL, this object structure will default to the string of "no match", which I can then check for, and display a nicer error message to help the user get back to a working place in the application.

javascript
const {
match: { params: { username } = { username: "no match" } },
} = this.props;

Default values are one, nifty way to guard against undefined errors in your destructured objects.


Conclusion

ES6 is a great improvement to the JavaScript syntax, and Airbnb’s ESLint configuration is an excellent linter to identify code where the latest and greatest in ES6 could be used but hasn’t been.

My biggest complaint is that sometimes it isn’t helpful enough in explaining just how to fix the errors it finds. Luckily, I found a resource to better explain how to destructure more deeply nested objects, and even how to set default values to prevent undefined errors from being thrown in my React applications.

Readability, clean, concise code, and error prevention with default values to fall back to, what more could one ask for? I’m a fan of object destructuring, especially now that I know how to handle all kinds of different scenarios with it.

Check back in a few weeks, I’ll be writing about JavaScript, ES6 or something else related to web development.

If you’d like to make sure you never miss an article I write, sign up for my newsletter here: https://paigeniedringhaus.substack.com

Thanks for reading, I hope this helps you better understand the power of ES6 object destructuring and how to use it in your own JavaScript applications.


Further References & Resources

Top comments (0)