frontend
Use ES6 To Destructure Deeply Nested Objects in JavaScript
Because Airbnb's ESLint is merciless in React.
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? 😫
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
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:
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:
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.
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?
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)
.
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
const myObject = {
props: "Hello world"
};
The destructured version becomes:
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:
const myObject = {
props: {
match : "Some value"
}
};
The destructured version becomes:
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:
const myObject = {
props: {
match: {
params: "A new value"
}
}
};
The new destructured version becomes:
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:
const myObject = {
props: {
match: {
params: {
username: "Paige"
}
}
}
};
The new destructured version becomes:
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.
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
const myObject = {
props: "I only have props, nothing else"
};
And the destructured version:
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
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
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.
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.
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. Please share this with your friends if you found it helpful!
References & Further Resources
- MERN, User Registration Github repo
- MDN Documentation object destructuring
- react-router-dom, npm
- MDN Documentation, object default values
Want to be notified first when I publish new content? Subscribe to my newsletter.