circuit

How to use async function in React hook useEffect (Typescript/JS)?




At first glance, you could have the idea to do something similar to get your content from a remote API as an example.

const MyFunctionnalComponent: React.FC = (props) => {
  useEffect(async () => {
   await loadContent();
  }, []);

  return <div></div>;
}

🤔 What's wrong with that?

If you're using Typescript, the compiler should be yielding something like this :

Argument of type '() => Promise<void>' is not assignable to parameter of type 'EffectCallback'.

Let's see why this error appears by taking the definition of what an async function is:

A function that allows to use asynchronous instructions with the await keyword which will block the statement execution as long as the Promise after which the await keyword is doesn't resolve…

All right seems great… but wait…

This function will also return a Promise, no matter if you explicitly return something or not. In case you return data, it will be wrapped in the resolving content of the promise that the function will create and return automatically.

Huh! Did you start seeing the problem here? No? Let's read about the useEffect hook here to get more information: https://reactjs.org/docs/hooks-reference.html#useeffect

Often, effects create resources that need to be cleaned up before the component leaves the screen, such as a subscription or timer ID. To do this, the function passed to useEffect may return a clean-up function. For example, to create a subscription.

📌 Using an async function makes the callback function return a Promise instead of a cleanup function.

And that's why the compiler is yielding in Typescript. This pattern is also not working in plain JS as React is not waiting for a promise.

💡How to deal with asynchronous code in useEffect ?

By using the following technique, we will be able to use async function in our effects :

const MyFunctionnalComponent: React.FC = props => {
  useEffect(() => {
    *// Create an scoped async function in the hook
    *async function anyNameFunction() {
      await loadContent();
    }

*    // Execute the created function directly
    *anyNameFunction();
  }, []);

return <div></div>;
};

Now, your code is safe as you are returning nothing at all and the compiler has stopped yielding.

🎬 Bonus / TLDR; 📦

You can also use an IIFE, which has the same effect as above.

If you don't know what an IIFE is: it is described here by my Friend Sunil Sandhu.

const MyFunctionnalComponent: React.FC = props => {
  useEffect(() => {
    *// Using an IIFE
    *(async function anyNameFunction() {
      await loadContent();
    })();
  }, []);

  return <div></div>;
};



Continue Learning