Destructuring objects and arrays in JavaScript

JavaScript
React

Contents

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.

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.

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. The let 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 that metadata 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's title property and assign it to a variable called title, and
    • take the value assigned to the metadata object's category property and assign it to a variable called category

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'

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.

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 called topping1
  • the second element in the array (toppings[1]) is assigned to a variable called topping2

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.

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 called posts, and
  • assign the second element of the array that is returned by the call to useState() to a variable called setPosts

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()