DEV Community

Cover image for How to use async function in useEffect?
Jasmin Virdi
Jasmin Virdi

Posted on

How to use async function in useEffect?

In React we all must have used useEffect hook which runs after performing DOM updates and helps us to perform some operation after render.

Before exploring different ways to make async calls inside useEffect let's discuss the problem behind it.

Why we should not use async keyword with useEffect?

Let's take an example to understand this.

const [state, setState] = useState(0)

useEffect(async () => {
    await setState((state) => state + 1);
}, []);
Enter fullscreen mode Exit fullscreen mode

The above piece of code will give error because the async function returns promise and the useEffect doesn't expect callback function to return promise. It should return nothing or a function.

How we can call an asynchronous function inside useEffect? πŸ€”

In order to make the async call inside useEffect hook we can use the following approaches.

  • Defining async function inside useEffect.
 useEffect(() => {
   const fetchData = async()=> {
     const data = await getData()

     return data
   }
   fetchData()
 }, []);
Enter fullscreen mode Exit fullscreen mode
  • Defining async function outside useEffect.
  const [state, setState] = useState(0)

  const fetchData = useCallback(async()=> {
    const data = await getData();
    setState(data)
  }, [])

  useEffect(() => {
    fetchData()
  }, [fetchData]);
Enter fullscreen mode Exit fullscreen mode

In this case we need to wrap our async function in useCallback to map it with dependency array.

Note - If we do not wrap the function using useCallback hook it will re-render on every update which will result in triggering the useEffect hook again.

I have used these two approaches to use async function with useEffect. Feel free to add any important point or another approach with respect to this topic. πŸ™ŒπŸ»

Happy Learning! πŸ‘©πŸ»β€πŸ’»

Discussion (12)

Collapse
lukeshiru profile image
Luke Shiru

You shouldn't be using async in useEffect, that's why React "yells" at you when you try to do it. I'll recommend you take a look at the new beta docs about effects, but long story short is that is not recommended to do fetch or other async actions in effects. Almost always, there are better ways of doing something that doesn't require an effect. Dan is currently working on a new section that showcases scenarios in which folks generally use useEffect and they shouldn't.

Cheers!

Collapse
jasmin profile image
Jasmin Virdi Author • Edited on

Thanks for sharingπŸ™Œ

Agreed, this is not recommended. Wanted to throw some light on ways with reasoning.

Collapse
peerreynders profile image
peerreynders

There was this talk, "When To Fetch", recently at Reactathon 2022

"You need to initiate fetches before you render"

Collapse
hbgl profile image
hbgl

Using async from within hooks is completely fine. Yes, it is true that using a hook may not always be the best choice and that you should generally avoid them. However when you need to use an effect and when you need to do something asynchronously, then you should put your async code into the effect handler.

Collapse
lukeshiru profile image
Luke Shiru

If you do that, you end up with cascading instead of parallel fetching. There's a reason why React 18 in strict mode runs effects twice in dev: It does that so it actually "breaks" your code if you're using effects incorrectly and you fix it in dev. I recommend you take a look at the links I shared (both are completely straight from the React team), and you might also find useful reading about React Suspense and following the latest tweets from Dan Abramov talking about effects.

Trust me, the less async code you have in effects, the better your app will behave ☺️

Thread Thread
hbgl profile image
hbgl

The link you shared has an example on how to asynchronously fetch data inside useEffect: beta.reactjs.org/learn/synchronizi...

Thread Thread
lukeshiru profile image
Luke Shiru • Edited on

If you scroll like 2cm, you reach a "Deep dive" block with the title "What are good alternatives to data fetching in Effects?" and the first line in it:

Writing fetch calls inside Effects is a popular way to fetch data, especially in fully client-side apps. This is, however, a very manual approach and it has significant downsides

If you prefer the video format, @peerreynders shared this in another comment that you might find useful:

I'm not saying you can't do fetch on effects or render (obviously you can), I'm saying you shouldn't.

Thread Thread
hbgl profile image
hbgl

Thanks for sharing this very interesting talk. Using the loader api from react-router should definitely be preferred over fetching in the useEffect hook. But I still don't buy the full 'you shouldn't be using async in useEffect' thing because I don't see the fundamental difference between a sync and async effect. An effect is an effect is an effect. I mean look at the VideoPlayer example from the page you linked:

import { useEffect, useRef } from 'react';

function VideoPlayer({ src, isPlaying }) {
  const ref = useRef(null);

  useEffect(() => {
    if (isPlaying) {
      ref.current.play();
    } else {
      ref.current.pause();
    }
  });

  return <video ref={ref} src={src} loop playsInline />;
}
Enter fullscreen mode Exit fullscreen mode

HTMLMediaElement.play() returns a Promise. Why would it be wrong to make the effect async and await the video being played? React surely doesn't "yell" at you as long you do the proper cleanup.

Thread Thread
brense profile image
Rense Bakker

You can use async side effects but you would have to use React suspense. Using async side effects without React suspense is an antipattern and should be avoided because A. it makes your app really difficult to debug and B. it has the potential to cause an infinite render loop.

Collapse
ivanjeremic profile image
Ivan Jeremic • Edited on

You should not do async stuff? So react-query does everything wrong under the hood you say?

Collapse
lukeshiru profile image
Luke Shiru

πŸ˜‚ react-query wraps async functions, but you don't use async directly, and I was talking about using async stuff inside effects.

Collapse
kouliavtsev profile image
kouliavtsev

You can also wrap the fucntion into a IIFE.

useEffect(() => {
    async () => {
          // Do stuff
    }();
}, []);
Enter fullscreen mode Exit fullscreen mode