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?
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.
- Observables and Async Pipe
- Identity Checking and Performance
- Web Components <ng-template> syntax
- <ng-container> and Observable Composition
- Advanced Rendering Patterns
- 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!