React TypeScript 16.8 How to make useEffect() async
Asked Answered
C

4

74

Why can't useEffect() use async-await?

const Home: React.FC = () => {
    
    useEffect(async () => {
        console.log(await ecc.randomKey())
    }, [])
    
    return (
    ...

The error I get is

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

Type 'Promise' is not assignable to type 'void | (() => void | undefined)'.

Type 'Promise' is not assignable to type '() => void | undefined'.

Type 'Promise' provides no match for the signature '(): void | undefined'.ts(2345)

Callisthenics answered 28/7, 2019 at 7:49 Comment(0)
I
88

Declaring the effect as async function is not recommended. But you can call async functions within the effect like following:

useEffect(() => {
  const genRandomKey = async () => {
    console.log(await ecc.randomKey())
  };

  genRandomKey();
}, []);

More here: React Hooks Fetch Data

Ironsmith answered 28/7, 2019 at 8:7 Comment(2)
Be careful doing this.... async callbacks after await could return after a react component has been dismounted and if you touch any component state in that scenario react will crash and throw some nasty errors. Anytime you are doing async things in a useEffect etc you should be checking if the component has unmounted before touching state.Floorman
I'm getting an IDE warning saying Promise returned from asyncMethod is ignored. Is this mechanism really safe?Unaccountedfor
P
41

You can use an IIFE (asynchronous anonymous function which executes itself) like so:

useEffect(() => {
    // Some synchronous code.

    (async () => {
        await doSomethingAsync();
    })();

    return () => {
        // Component unmount code.
    };
}, []);
Protochordate answered 13/9, 2020 at 1:27 Comment(1)
For those who don't know what it's called, it is IIFE (Immediately Invoked Function Expression) and here's the MDN link developer.mozilla.org/en-US/docs/Glossary/IIFERhetorician
P
1

You can use the use-async-effect package which provides a useAsyncEffect hook:

useAsyncEffect(async () => {
  await doSomethingAsync();
});
Protochordate answered 17/8, 2020 at 2:46 Comment(1)
Not a great solution since it only fixes one specific case of a problem that arise in many other cases.Grattan
S
0

I read the answers and all the answers are correct but the "native ones" (the ones not using a third-party package) don't allow you to do two things:

  1. Optimize the effect handler using useCallback (because a hook can only be used on the top level)
  2. Reuse the effect handler in other places.

I found this method which enables both

const Component= ()=>{
  const effectHandler = async()=>{
    // Do some async stuff
   }

  useEffect(()=>{
      effectHandler()
   }, [])


  // reuse effectHandler
   effectHandler()

  return(<></>)
}

Sharpset answered 2/5 at 18:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.