How to go async with useEffect

Execute asynchronous tasks with useEffect clean and safely

From calling an API to set a timeout delay, I am sure you already questioned how to do it properly when using the React hook useEffect . If that sounds still blurred, then this article is for you. If not, you may at least learn a different approach to get the job done or feel more confident while doing it.

I will present 3 ways of doing it safely and tidy. But first, I want to show what you should NOT do.

What you should NOT do ❌

Don’t do the async task directly in the useEffect, instead, ALWAYS wrap your async task into a function and only call it from the useEffect.

Here is an example of what you should NOT do:

useEffect(async () => {
await getData();
},[]);

The code above would block the effect, which is no good. The function passed into useEffect must remain strictly synchronous.

Also, doing what this BAD example showed, would lead to a React error:

Effect callbacks are synchronous to prevent race conditions.

Alright, now we are safe to move on to the good practices ⬇️

1. Defining the async function inside the hook

If you intend to execute an asynchronous task, to get some data from an API for example, and update the loading state of your component, you need to define the function inside the hook and then call it:

useEffect(() => {   async function getData() {
setLoading(false);
const reponse = await myApi.getSomeGoodData()
setLoading(true);
}
getData();},[]);

The code above is 100% correct and it will get the job done for you most of the time. However, let’s say you want to reuse the getData function in multiple effects. Then, check number #2.

2. Defining the async function outside of the hook

If you plan to reuse the asynchronous function you defined, then a good idea is to define it in your functional component body, wrapped by the useCallback hook. Check it out:

const getData = useCallback(() => {
setLoading(false);
const reponse = await myApi.getSomeGoodData()
setLoading(true);
}, [])
useEffect(() => { getData()},[getData])

Notice that getData needs to be passed in as a dependency for the useEffect hook.

3. Setting an interval or timeout

The 2 previous examples were mainly focused on fetching data and updating the component state. This one is to show also a common scenario for async tasks inside useEffect which is setting an interval or a timeout.

The trick for it is to ALWAYS clear the timeout and the interval set in the useEffect .

Setting a timeout

useEffect(() => {   const timeout = setTimeout(() => {
// do something
}, 100)
return () => {
clearTimeout(timeout);
}
},[])

Setting an interval

useEffect(() => {  const interval = setInterval(() => {
// do something
}, 100)
return () => {
clearInterval(interval);
}
},[])

Conclusion

The examples above will get you covered for 99.99% of the async situations you may encounter while React developing. Just remember to be mindful of the effect dependencies. If you didn’t find your case in the examples below, drop a comment and I will look into it.

Thanks for reading 🕺

—- Fernando Abolafio

I breath, I eat, I love and I code.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store