How To Use Async Await in React (componentDidMount Async)

How to use Async Await in React?

How To Use Async Await in React

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 regeneratorRuntime is not defined.

If you’re hitting that error and you want to use Async Await in React, look no further.

In this post we’ll see how to:

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

Ready?

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?

Javascript 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.

Let’s see how.

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

To start off clone my webpack repo (it’s a webpack 4 quickstart which includes React out of the box):

git clone git@github.com:valentinogagliardi/webpack-4-quickstart.git

Move into the directory and install the dependencies:

cd webpack-4-quickstart/ && npm i

Open up the directory with your favourite text editor and wipe out the content of App.js.

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.

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

(I know, who cares about IE?).

Anyway, using async/await in React requires no magic.

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?

Simple.

The key for fixing regeneratorRuntime not defined is babel-preset-env.

It is included in my webpack quickstart, but if you have an existing webpack boilerplate make sure to install it:

npm i babel-preset-env --save-dev

Open up .babelrc and update the configuration as follow:

{
    "presets": [
      ["env", {
        "targets": {
          "browsers": [
            ">0.25%",
            "not ie 11",
            "not op_mini all"
          ]
        }
      }], "react"
    ]
}

Check out Jamie Kyle’s last 2 versions harmful for learning more

Save the file and check out the browser.

It works!

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

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 see 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.

Introduce 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 by yourself.

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: Fetch does not reject the Promise in case of HTTP errors. 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.

create-react-app supports async/await out of the box.

But if you work with your own webpack boilerplate you may hit regeneratorRuntime is not defined.

The key for fixing regeneratorRuntime not defined? babel-preset-env and a bit of configuration.

From there you can use Async Await in React.

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 to use target browsers
  • 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 student of mine pointed out that you cannot use async/await in componentWillMount.

That’s true: you cannot return a Promise from componentWillMount.

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 with babel-preset-env and target browser.

The bundle’s size remains within acceptable limits.

Thanks for reading!

Hold tight until the next one!

One Reply to “How To Use Async Await in React (componentDidMount Async)”

  1. Hi Valentino, great tutorial!

    I can’t get the async/await to work though.
    I followed the steps above:

    git clone git@github.com:valentinogagliardi/webpack-4-quicksta.git
    cd webpack-4-quickstart
    npm i

    I updated the App.js code as above.

    $ cat .babelrc
    {
    “presets”: [
    [“env”, {
    “targets”: {
    “browsers”: [
    “>0.25%”,
    “not ie 11”,
    “not op_mini all”
    ]
    }
    }], “react”
    ]
    }

    But I still get the “Uncaught ReferenceError: regeneratorRuntime is not defined” error.
    Any idea? Maybe the list of browsers has changed since then?

    Cheers!
    Mark

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.