How To Use Async Await in React (componentDidMount Async)

How To Use Async Await in React?

How To Use Async Await in React (componentDidMount Async)

You want to use Async Await in React the same as you do in Node.JS? create-react-app supports async/await out of the box.

But if you work with your own webpack boilerplate you may hit an error like regeneratorRuntime is not defined. In the following post you’ll see how to use Async Await in React and how to fix that error.

You’ll learn how to:

  • fix regeneratorRuntime is not defined
  • use async/await in React with Fetch
  • handle errors with Fetch and async/await

Ready?

Disclaimer

React is changing fast and and the method exposed here could be already obsolete by the time you’ll see this article, originally wrote in June 2018. Take the post with a grain of salt. In particular you may want to investigate React Hooks, which have they’re own way for fetching data.

How To Use Async Await in React: what is async/await?

Let’s introduce a bit of async/await first. async/await in JavaScript is nothing more than syntactic sugar over Promises.

Why so? Are Javascript Promises not enough? Promises are fine, yet in some situations you may end up with a long chain of then/catch.

I would argue, if you find yourself in that position better you simplify the code, But async/await helps writing asynchronous code in a way that looks synchronous.

It makes your code cleaner and readable. Plus you can use try/catch for proper error handling. async/await is convenient and clean: at some point you may want to introduce it in your React components too!

Let’s see how.

How To Use Async Await in React: an example with Promises

Before starting off make sure you have a React development environment ready to roll. To make one you can follow this tutorial of mine: How to set up React, webpack, and Babel or you can also use create-react-app.

Now let’s play with Promises. Let’s say you want to fetch data from an API. It’s standard React stuff.

You put the API call in componentDidMount and that’s it:

// FILE: App.js
import React, { Component } from "react";
import ReactDOM from "react-dom";

class App extends Component {
  constructor() {
    super();
    this.state = { data: [] };
  }

  componentDidMount() {
    fetch(`https://api.coinmarketcap.com/v1/ticker/?limit=10`)
      .then(res => res.json())
      .then(json => this.setState({ data: json }));
  }

  render() {
    return (
      <div>
        <ul>
          {this.state.data.map(el => (
            <li>
              {el.name}: {el.price_usd}
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

export default App;

ReactDOM.render(<App />, document.getElementById("app"));

(The above component is a contrived example: there’s no error handling. Let’s assume we’re in the happy path and nothing goes wrong with our fetch call).

If you run webpack-dev-server with:

npm start

you’ll see the code working as expected (it’s ugly but it works):

How To Use Async Await in React: an example with Promises

Nothing fancy right?

Can we do better? Would be nice to use async/await in componentDidMount right?

Let’s try!

How To Use Async Await in React: using the async/await syntax

Supported since version 7.6.0 the async/await syntax is widely used in Node.Js. On the front-end it’s another story. async/await is not covered by all browsers. (I know, who cares about IE?).

Anyway, using async/await in React requires no magic. But where to put async/await in a React component?

Used mostly for data fetching and other initialization stuff componentDidMount is a nice place for using async/await in React.

Here are the steps to follow for using async/await in React:

  1. put the async keyword in front of your functions
  2. use await in the function’s body
  3. catch the errors

There’s one thing yet: async/await is not supported across all browsers and you must take care of a detail.

create-react-app supports async/await out of the box. But if you have a webpack boilerplate you may hit an error (we’ll see that in a second).

Now let’s apply async/await to our React component.

Open up App.js and adjust componentDidMount:

// FILE: App.js
import React, { Component } from "react";
import ReactDOM from "react-dom";

class App extends Component {
  constructor() {
    super();
    this.state = { data: [] };
  }

  async componentDidMount() {
    const response = await fetch(`https://api.coinmarketcap.com/v1/ticker/?limit=10`);
    const json = await response.json();
    this.setState({ data: json });
  }

  render() {
    return (
      <div>
        <ul>
          {this.state.data.map(el => (
            <li>
              {el.name}: {el.price_usd}
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

export default App;

ReactDOM.render(<App />, document.getElementById("app"));

No error catching, again, let’s assume nothing goes wrong with our fetch call. Take a look at the browser’s console.

Does it work?

How to use async await in React. regeneratorRuntime is not defined

regeneratorRuntime is not defined? What’s that? How to solve regeneratorRuntime is not defined so you can use async/await? The key for fixing that error are babel preset env and babel plugin transform runtime.

Make sure to install them:

npm i @babel/preset-env @babel/plugin-transform-runtime @babel/runtime --save-dev

Open up .babelrc and update the configuration as follow:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "browsers": [
            ">0.25%",
            "not ie 11",
            "not op_mini all"
          ]
        }
      }
    ],
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-transform-runtime"
  ]
}

Save the file (run the build again if you don’t have webpack-dev-server) and check out the browser. It should works again! Nice and clean but we’re not done! How about errors?

What happens if the user goes offline or the API goes down? In the next section we’ll se how to handle errors with Fetch and async/await.

How To Use Async Await in React: handling errors

The example we saw so far doesn’t handle errors:

  async componentDidMount() {
    const response = await fetch(`https://api.coinmarketcap.com/v1/ticker/?limit=10`);
    const json = await response.json();
    this.setState({ data: json });
  }

Granted, in real world apps you would decouple fetch calls from the view. Moreover, the fetch API has some caveats when it comes to handling errors.

TJ VanToll has a nice writeup: Handling Failed HTTP Responses With fetch(). So, how can we make our code more reliable? Let’s experiment with our component.

Create an error by removing coinmarketcap from the url:

  async componentDidMount() {
    const response = await fetch(`https://api.com/v1/ticker/?limit=10`);
    const json = await response.json();
    this.setState({ data: json });
  }

Run the code and check the console. You get:

  • TypeError: NetworkError when attempting to fetch resource, in Firefox
  • Uncaught (in promise) TypeError: Failed to fetch, in Chrome

There is an uncaught error of course. Let’s catch it.

Add a try/catch block:

  async componentDidMount() {
    try {
      const response = await fetch(`https://api.com/v1/ticker/?limit=10`);
      const json = await response.json();
      this.setState({ data: json });
    } catch (error) {
      console.log(error);
    }
  }

And re-run the code. You’ll see the error logged to the console.

So here’s the first thing: wrap fetch inside a try/catch block to handle network errors.

Now let’s try another thing. If you read TJ VanToll article you won’t be surprised by the next example.

Check this out:

  async componentDidMount() {
    try {
      const response = await fetch(`http://httpstat.us/500`);
    } catch (error) {
      console.log(error);
    }
  }

What do you see in the console? Nothing. No errors, no sign of life. Why?

The bad news: fetch will only reject a Promise if there is a network error. User is offline, DNS troubles.

In case of 404 or 500 you’ll see no errors. That means you must check the response.

The good news: fetch responses carry a property (called ok) with a true/false value depending on the HTTP response.

In our case you would handle the error as follow:

  async componentDidMount() {
    try {
      const response = await fetch(`http://httpstat.us/500`);
      if (!response.ok) {
        throw Error(response.statusText);
      }
    } catch (error) {
      console.log(error);
    }
  }

Now the error is logged to the console as expected.

At this point you can show the user some error message or other meaningful stuff.

So here’s the second thing to know: fetch does not reject the Promise in case of HTTP errors. You must check the ok property of the response object.

Coming back to our example, the complete code would be:

  async componentDidMount() {
    try {
      const response = await fetch(`https://api.coinmarketcap.com/v1/ticker/?limit=10`);
      if (!response.ok) {
        throw Error(response.statusText);
      }
      const json = await response.json();
      this.setState({ data: json });
    } catch (error) {
      console.log(error);
    }
  }

This version deals with errors and provides a solid starting point.

Again, in real life you would decouple fetch calls from the view but that’s another story.

For learning more about Promise rejection in Async Functions check out How to Throw Errors From Async Functions in Javascript

How To Use Async Await in React: wrapping up

Supported since version 7.6.0 the async/await syntax is widely used in Node.Js. async/await can make your code cleaner and readable.

The syntax is convenient and you may want to introduce it in your React components. Where?

Used mostly for data fetching and other initialization stuff componentDidMount is a nice place for using async/await in React.

Here are the steps you need to follow for using async/await in React:

  • configure babel
  • put the async keyword in front of componentDidMount
  • use await in the function’s body
  • make sure to catch eventual errors

If you use Fetch API in your code be aware it has some caveats when it comes to handling errors.

And you’re all set!

How To Use Async Await in React: frequently asked questions

componentDidMount is the natural place for using async/await in React. After all you want to fetch data from APIs as soon as the component mounts.

A reader pointed out that you cannot use async/await in componentWillMount. That’s true: you cannot return a Promise from that lifecycle emthod. Anyway, I wouldn’t bother about componentWillMount. It’s one of the three lifecyle methods that will gradually fade away from React.

Another common question when using Async Await in React is the bundle size. In the recent past there was no way to use async/await for the browser without adding babel polyfill.

The resulting code? An enormous bundle. Now that’s no longer the case if you configure babel well. The bundle’s size remains within acceptable limits.

Thanks for reading and stay tuned on this blog!