Get super confident at React. React Icon

I promise to change your React skills!

Using Async Await Inside React's useEffect() Hook

In this post you’ll learn how to use an async function inside your React useEffect hook.

Perhaps you’ve been using the good old Promise syntax with a .then() method chain.

Let’s take a Promise-based refactor things out and investigate how to use async/await functions with React’s useEffect hook, as we could easily slip up and cause ourselves some headache without knowing a few key points.

Below, we’re setting some initial state as [] via a useState hook, which of course we’ll want to populate via a GET request when the component mounts.

After the component mounts, we let a useEffect hook spin off a quick fetchUsers() call which returns us a Promise type, which we evaluate via .then() and grab the data inside and use the setUsers() setter function to populate our users value.

const Users = () => {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetchUsers().then((users) => setUsers(users));
  }, []);

  if (!users) return <div>Loading...</div>;

  return (
    <ul>
      {users.map((user) => (
        <li>{user.name}</li>
      ))}
    </ul>
  );
};

With this in mind, we’ll want to introduce async as a function, and combine it with an await statement, right? Not so fast my friend. On the surface that approach would work, but there are some hidden trapdoors that we could step on if we blindly go refactoring - hence the creation of this blog post.

// ❌ Don't do this!
useEffect(async () => {
  const users = await fetchUsers();
  setUsers(users);

  return () => {
    // this never gets called, hello memory leaks...
  };
}, []);

This WORKS, but you should avoid it. Why? Because React’s useEffect hook expects a cleanup function returned from it which is called when the component unmounts. Using an async function here will cause a bug as the cleanup function will never get called. Yikes! So what do we do?

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

Simply put, we should use an async function inside the useEffect hook. There are two patterns you could use, an immediately-invoked function expression (my preferred approach), or a named function that you invoke. Let’s compare, and you can decide what you prefer.

// 🆗 Ship it
useEffect(() => {
  (async () => {
    const users = await fetchUsers();
    setUsers(users);
  })();

  return () => {
    // this now gets called when the component unmounts
  };
}, []);

Or you can go for a named function:

// 🆗 Ship it
useEffect(() => {
  const getUsers = async () => {
    const users = await fetchUsers();
    setUsers(users);
  };

  getUsers(); // run it, run it

  return () => {
    // this now gets called when the component unmounts
  };
}, []);

Either way, we’re now safe to use async functions inside useEffect hooks. Now if/when you want to return a cleanup function, it will get called and we also keep useEffect nice and clean and free from race conditions.

Enjoy using async functions with React’s useEffect from here on out!

💯 If you are serious about your React skills, your next step is to take a look at my React courses where you’ll learn React, React Router, Styling and CSS practices, advanced concepts and APIs as well as server-side rendering and much more.

Happy awaiting!

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover