Carl Rippon

Building SPAs

Carl Rippon
BlogBooks / CoursesAbout
This site uses cookies. Click here to find out more

Clean up your code with destructuring

July 30, 2019
javascript

Photo by Vullioud Pierre-André https://www.freeimages.com/photographer/zebulon666-32150

Destructuring is a neat way of cleaning up JavaScript code. In this post we’ll go through lots of different areas destructuring helps the readability of our code.

Function parameters

When a function parameter is an object rather than a primitive, destructuring can be used to break the object into the parts that the function references. The code below is without any destructuring:

async function logData(options) {
  if (!options.skip) {
    const response = await fetch(options.path);
    const data = await response.json();
    console.log(data);
  }
}

This can be refactored into the following by destructuring the options parameter:

async function logData({ skip, path }) {
  if (!skip) {
    const response = await fetch(path);
    const data = await response.json();
    console.log(data);
  }
}

This is arguably cleaner because we are referencing the parameter properties, skip and path, directly rather than through the options parameter name.

A really neat thing about this approach is that we can apply default values to the destructured properties. Without destructuring we’d have some like this:

async function logData(options) {
  const path = options.path || "https://swapi.co/api/people/1";
  if (!options.skip) {
    const response = await fetch(path);
    const data = await response.json();
    console.log(data);
  }
}

… but with destructuring, its a little cleaner:

async function logData({ skip, path = "https://swapi.co/api/people/1" }) {
  if (!skip) {
    const response = await fetch(path);
    const data = await response.json();
    console.log(data);
  }
}

Apart from saving a line of code, having the default value for the parameter right next to its declaration helps us understand the parameter a little easier.

This approach is useful in React function components for the components props:

export const CountDown = ({ start = 10 }) => {
  const [count, setCount] = useState(start);
  ...
};

start is a prop in CountDown component in the above example.

A rest parameter can be used to collect properties that haven’t been destructured. This is useful for passing through props to lower level elements in React components:

const Field = ({ id, label, value, type = "text", onChange, ...rest }) => {
  return (
    <div>
      <label for={id}>{label}</label>
      <input type={type} id={id} value={value} onChange={onChange} {...rest} />
    </div>
  );
};

Destructuring can be applied to parameter properties that themselves are objects as well. The following is an example without destructuring:

async function sendEmail(config) {
  const response = await fetch(config.api.path, {
    method: "post",
    body: JSON.stringify({
      emailAddress: config.contact.emailAddress,
      template: config.template
    })
  });
  return response.ok;
}

… which can be refactored to the following:

async function sendEmail({
  api: { path },
  contact: { emailAddress },
  template
}) {
  const response = await fetch(path, {
    method: "post",
    body: JSON.stringify({
      emailAddress,
      template
    })
  });
  return response.ok;
}

… and with some defaults:

async function sendEmail({
  api: { path = "https://some-email-server" },
  contact: { emailAddress },
  template = "standard"
}) {
  const response = await fetch(path, {
    method: "post",
    body: JSON.stringify({
      emailAddress,
      template
    })
  });
  return response.ok;
}

We can use TypeScript to strongly-type destructured elements as follows:

async function logData({
  skip = false,
  path = "https://swapi.co/api/people/1"
}: {
  skip?: boolean,
  path?: string
}) {
  if (!skip) {
    const response = await fetch(path);
    const data = await response.json();
    console.log(data);
  }
}

… or we can extract the type out of the function:

type LogDataOptions = {
  skip?: boolean,
  path?: string
};
async function logData({
  skip = false,
  path = "https://swapi.co/api/people/1"
}: LogDataOptions) {
  if (!skip) {
    const response = await fetch(path);
    const data = await response.json();
    console.log(data);
  }
}

Function call result

The result of a call to a function can be destructured. So, without destructuring we may have something like this:

const character = await getCharacter(1);
console.log(character.name);

… which can be refactored to the following with destructuring:

const { name } = await getCharacter(1);
console.log(name);

Defaults can be applied in the same way as for function parameters:

const { name = "Unknown" } = await getCharacter(1);
console.log(name);

The destructured elements can be strongly-typed with TypeScript as well:

const { name = "Unknown" }: { name: string } = await getCharacter(1);
console.log(name);

… or using a reusable type:

type Character = {
  name: string;
  height: string;
  gender: "male" | "female";
  ...
}
...
const { name = "Unknown" }: Character = await getCharacter(1);
console.log(name);

Destructured items can be renamed as follows:

const { name: characterName } = await getCharacter(1);
console.log(characterName);

… and with a TypeScript type annotation:

const { name: characterName }: { name: string } = await getCharacter(1);

Notice that the type represents the data before it’s property been renamed.

Arrays can also be destructured. In fact, this is used to consume React hooks:

count is the first array item returned by the useState hook and setCount is the second.

Array items can also be skipped in the destructuring process:

const [, , z] = getPoint();

In the above example we skip past the first two array items and assign the third item to z.

Wrap up

Destructuring syntax may seem weird at first but you quickly get used to it and it’s a neat little way of making code a little cleaner and more readable.

You might find some of my other posts interesting:
Upgrading npm dependencies

Want more content like this?

Subscribe to receive notifications on new blog posts and courses

Required
© Carl Rippon
Privacy Policy