React Icon Get 67% off the React Master Bundle!

See the bundle then add to cart and your discount is applied.

0 days
00 hours
00 mins
00 secs
React

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.

Assuming the below example, 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?

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:

JavaScript Array Methods eBook Cover

Ready to go beyond ForEach? Get confident with advanced methods - Reduce, Find, Filter, Every, Some and Map.

NestJS Build a RESTful CRUD API eBook Cover

Build your first NestJS app. With the CLI you'll learn the basics of real-world NestJS development.