Skip to content

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Executing Promises in a React Component

When you need to execute asynchronous code in a React component, that usually involves a JavaScript promise.

Feb 23, 2020 • 6 Minute Read

Introduction

When you need to execute asynchronous code in a React component, that usually involves a Javascript promise. Making sure your component updates when a promise completes isn't entirely obvious at first, so in this guide I will show you how to work with promises in a React component.

Handling Promises in Class Components

When creating components in React, there are two options: class components or functional components. Using a class component means your component will automatically have the idea of state, as well as component lifecycle methods.

      import React from "react";

class UserComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      email: "",
      password: ""
    };
  }

  componentDidMount() {}

  componentShouldUpdate() {}

  componentWillUnmount() {}

  render() {
    return (
      <div>
        <h1>A user</h1>
      </div>
    );
  }
}
    

The component lifecycle methods are important because they are where you'll want to handle asynchronous code. Specifically, the componentDidMount method can be used to update your component with new state when a promise resolves. Here's how to do that:

      import React from "react";

class UserComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      email: "",
      password: ""
    };
  }

  componentDidMount() {
    const fetchUserEmail = async () => {
      const response = await fetch("/emails");
      const { email } = await response.json();
      this.setState({
        email
      });
    };
    fetchUserEmail();
  }

  render() {
    return (
      <div>
        <h1>A user</h1>
        <p>{this.state.email}</p>
      </div>
    );
  }
}
    

In the above code snippet I am fetching emails from an imaginary endpoint that returns an object with an email property. I am using the async/await syntax to handle the promise that fetch returns, as well as the promise returned by calling json() on the response. I destructured the email property from the resulting json and I call this.setState to make sure we update the component with the new value for this.state.email.

It's not required to use componentDidMount to handle promises. Use componentDidMount when you only want the promise to execute a single time as the component loads. You could execute the same promise when a use clicks a button. For example:

      import React from "react";

class UserComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      email: "",
      password: ""
    };
  }

  handleButtonClick = () => {
    const fetchUserEmail = async () => {
      const response = await fetch("/emails");
      const { email } = await response.json();
      this.setState({
        email
      });
    };
    fetchUserEmail();
  };

  render() {
    return (
      <div>
        <h1>A user</h1>
        <p>{this.state.email}</p>
        <button onClick={this.handleButtonClick}>Fetch Email</button>
      </div>
    );
  }
}
    

Handling Promises with useEffect Hook

If instead of using a class component you go with a functional component, the process is very similar. The biggest differences are in how lifecycle is handled and how to use state. Functional components don't have lifecycle methods, but they do let you control when a component updates. Instead of thinking, "How do I replicate updating a component when it mounts?" we can think, "How do I update the component when some value changes?" For this functionality, we'll use the useEffect hook. There is no this.setState in a functional component, either. We are able to use the useState hook to give your component state. Here's how that would look:

      import React from "react";

const UserComponent = props => {
  const [email, setEmail] = React.useState("");
  React.useEffect(() => {
    const fetchUserEmail = async () => {
      const response = await fetch("/emails");
      const { email } = await response.json();
      setEmail(email);
    };
    fetchUserEmail();
  }, []);

  return (
    <div>
      <h1>A user</h1>
      <p>{email}</p>
    </div>
  );
};
    

The useEffect hook takes two arguments: a function as the first argument and a dependency array as the second. The dependency array takes a list values, and when any of those values changes, the function you passed as the first argument will run. If you don't pass any values to the dependency array, the function will only run once when the component loads. This is similar behavior to the componentDidMount lifecycle method.

You are creating a state variable called email with this line:

      const [email, setEmail] = React.useState("");
    

useState always returns an array with two values: the current state value and an updater function, always in that order. This means we can destructure that array as we are doing above. Passing the empty string to useState means we are defaulting the value of email to an empty string. Calling setEmail is just like calling this.setState in a class component. The component will rerender with the new email value.

Similar to the class component example, you don't always have to handle promises in the useEffect hook.

      import React from "react";

const UserComponent = props => {
  const [email, setEmail] = React.useState("");

  const fetchUserEmail = async () => {
    const response = await fetch("/emails");
    const { email } = await response.json();
    setEmail(email);
  };

  return (
    <div>
      <h1>A user</h1>
      <p>{email}</p>
      <button onClick={fetchUserEmail}>Fetch Email</button>
    </div>
  );
};
    

In the above example, we are firing off the fetch promise on click of the "Fetch Email" button. When the promise resolves, we'll call setEmail with the new email address.

Conclusion

There are slight differences when working with promises between class components and functional components. In this guide, we covered two common situations: handling promises on component mount and handling promises when a user takes an action. Now that you learned how to use them, I hope you try adding promises to your next project 😊.

Checkout my course Javascript Generators and Iterators to learn more about the Javascript core language.

Marques Woodson

Marques W.

Marques has been involved with software development for years, specializing in Javascript application architecture, hybrid mobile application development, and Node.js applications. As a family man living in Chicago, he's had the chance to work with large enterprises doing legacy code optimization and refactoring, and startups building from the ground up. I'm passionate about experimenting with Javascript frameworks and libraries and figuring out what would work best for my current team/project. He also really enjoys teaching and mentoring new developers.

More about this author