Destructuring objects and arrays in JavaScript
Contents
Introduction
It can be tempting for newcomers to the world of front end development to jump straight into learning React without first gaining an appreciation of the fundamental aspets of JavaScript that the React library is leveraging. I think that is a mistake that will limit your ability to become comfortable with the more complex aspects of React.
This is the first in a series of posts focusing on some of those fundamentals that we come across time and again in React applications. Here we are delving into the JavaScript destructuring assignment and how it applies to objects and arrays.
What is destructuring?
Destructuring is an assignment operation that assigns object properties or array elements to distinct variables.
It was introduced to the language with ES6 in or around 2015.
Object destructuring
The best way to understand what's going on in a destructuring operation is to dive in and look at some examples. We will start with object destructuring.
Imagine we have this object representing a blog post's metadata
const metadata = { title: 'Object destructuring', subtitle: 'A post about object destructuring', category: 'fundamentals', tags: ['javascript', 'react'], isFeatured: true }
We can assign the metadata
object's properties to variables using dot notation
const title = metadata.title const subtitle = metadata.subtitle const category = metadata.category const tags = metadata.tags const isFeatured = metadata.isFeatured
That's the longhand way of doing it.
The destructuring operation gives us a much neater, more concise way of doing the same thing:
const { title, subtitle, category, tags, isFeatured } = metadata console.log(title) // 'Object destructuring' console.log(subtitle) // 'A post about object destructuring' console.log(category) // 'fundamentals' console.log(tags) // ['javascript', 'react'] console.log(isFeatured) // true
- the string 'Object destructuring' is assigned to the variable
title
- the string 'A post about destructuring' is assigned to the variable
subtitle
- and so on...
What's going on here?
We can tell that it's an assignment operation because we have an equals sign with expressions on either side of it but the syntax is a little different from a regular assignment. Let's break it down.
- the
const
keyword indicates we are defining constant variables. Thelet
keyword can also be used if we want to be able to re-assign the varaibles. - it's similar to a regular assignment operation in many ways:
- on the left side of assignment operator we define the variable names
- and on the right we define values to assign to the variables
Examining the right hand side first - ie. the values - this is simply the name of the object that we are destructuring, in this case: metadata
.
- so this is telling JS that we want to destructure the
metadata
object to access the values assigned to its properties
Now let's examine the left side of the assignment operation - ie. the variable names
- here we have a comma-separated list of the object properties that we want to extract
- the fact that the list is wrapped in
{ }
tells JS thatmetadata
is an object - this list doesn't have to include all of the object's properties
- we can just list the ones we want to access
- eg
const { title, category } = metadata
- so, for example,
const { title, category } = metadata
is telling JS:- take the value assigned to the
metadata
object'stitle
property and assign it to a variable calledtitle
, and - take the value assigned to the
metadata
object'scategory
property and assign it to a variable calledcategory
- take the value assigned to the
What if we want to destructure a value but name the variable differently?
Say we want to assign the value from the subtitle
property to a variable called excerpt
. We can do that like this:
const { subtitle: excerpt } = metadata
Destructuring nested objects
What if our metadata
object is itself a property of another object? Something like this maybe:
const post = { metadata: { title: 'Object destructuring', subtitle: 'A post about object destructuring', category: 'fundamentals', tags: ['javascript', 'react'], isFeatured: true }, content: 'This is the content of the blog post', }
content
is a 'top-level' property so it can be extracted with
const { content } = post console.log(content) // 'This is the content of the blog post'
Properties of the nested metadata
object can be accessed in two ways:
-
either use dot notation to drill down into the object on the right side of the assignment
const { tags } = post.metadata console.log(tags) // ['javascript', 'react']
-
OR use nested curly braces to drill down on the left side of the assignment
const { metadata: { tags } } = post console.log(tags) // ['javascript', 'react']
And if we want to extract a nested property and rename the variable in the process:
const { metadata: { subtitle: excerpt } } = post console.log(excerpt) // 'A post about destructuring'
Object destructuring in React
We come across object destructuring often in React. Two of the most common uses are:
- Import statements
- Props
Import statements
import { useState, useEffect } from 'react'
Values exported from modules (that are not the default export) are wrapped in an object. So this statement is saying:
"Import the useState
and useEffect
properties from the 'react' module's exported object."
Props
You tend to see props
values defined in one of two ways:
-
props values destructured in a separate line:
const SinglePost = (props) => { const { post } = props // component code }
or
-
props destructured within the parentheses of the function declaration:
const SinglePost = ({ post }) => { // component code }
Both methods achieve the same end result. Which you use is largely a question of personal (or team) choice.
Hooks that return objects
A third use of object destructuring in React is to extract values from objects that are returned from some Hooks. The useContext
Hook, for example, returns an object and it is customary to use object destructuring to assign values from that context object to distinct variables.
You might see something like this at the top of a component:
const { users, isLoading, fetchUsers } = useContext(UsersContext)
This makes the variables users
(presumably an array of users), isLoading
(a boolean) and fetchUsers
(a function that fetches an array of users) available in the component.
Array destructuring
Some examples will also help to explain how array destructuring works. We are going to use everybody's favourite array: an array of pizza toppings.
const toppings = [ 'mushrooms', 'tomatoes', 'green peppers', 'red peppers', 'sweetcorn', 'red onion', 'olives', 'jalapenos', 'artichokes' ]
Example 1: destructure the first two elements
If we want to assign the first two elements to distinct variables we can do that:
const [ topping1, topping2 ] = toppings console.log(topping1) // mushrooms console.log(topping2) // tomatoes
What's going on here?
The statement looks very similar to an object destructuring assignment. Again, we know it's an assignment operation because of the =
. We can use the let
keyword instead of const
if we want to be able to re-assign the defined variables.
The expression on the left side of the =
is wrapped in square brackets because we are dealing with an array.
The value on the right of the =
again represents the value being destructured, in this case the array.
Where array destructuring differs from object destructuring is that the elements to be extracted are determined by position rather than property name. That's because array elements don't have property names; they only have an index.
We always have to provide variable names in array destructuring assignments. And it is the position that that variable occupies in the list of elements within the square brackets that determines which element is assigned to that variable. So, in the example above:
- the first element in the array (
toppings[0]
) is assigned to a variable calledtopping1
- the second element in the array (
toppings[1]
) is assigned to a variable calledtopping2
Example 2: destructure elements one, three & five
const [ topping1,, topping3,, topping5 ] = toppings console.log(topping1) // mushrooms console.log(topping3) // green peppers console.log(topping5) // sweetcorn
By providing no variable name in the second & fourth positions we tell JavaScript to skip those elements.
Example 3: destructure elements one, three & five and assign the remainder to their own variable
const [ topping1,, topping3,, topping5, ...rest ] = toppings console.log(topping1) // mushrooms console.log(topping3) // green peppers console.log(topping5) // sweetcorn console.log(rest) // [ 'red onion', 'olives', 'jalapenos', 'artichokes' ]
This makes use of a desctructuring rest parameter (...rest
), a special parameter that tells JavaScript to bundle up all of the remaining elements into a separate array and assign that array to a variable with the name that follows the three dots. The variable can be called whatever you like (within normal variable-naming restrictions); it doesn't have to be rest.
Array destructuring in React
You will come across array destructuring whenever you use React Hooks. The first Hook that you use when learning React - useState
- returns an array containing two elements:
- state, and
- a function that we call to set state.
It's customary to destructure those elements into distinct variables. Something like this:
const [ posts, setPosts ] = useState([])
You now know that this destructuring assignment is telling JavaScript to:
- assign the first element of the array that is returned by the call to
useState()
to a variable calledposts
, and - assign the second element of the array that is returned by the call to
useState()
to a variable calledsetPosts
Conclusion
When I first encountered JavaScript destructuring assignments I found the syntax a little confusing; everything seemed to be back to front. My aim in this post has been to explain what the syntax means and to give examples of ways that destructuring can be applied.
Further posts in this series will look at some other JavaScript fundamentals that are common features in React applications:
- the spread operator
- arrow functions
- Array iterator methods, including:
Array.map()
Array.filter()
Array.find()
&Array.findAll()
Array.reduce()
Array.sort()