Definitelytyped: [@types/react] useEffect(async () => ...) does not account for async

Created on 15 Nov 2018  路  13Comments  路  Source: DefinitelyTyped/DefinitelyTyped

If you know how to fix the issue, make a pull request instead.

  • [x] I tried using the @types/xxxx package and had problems.
  • [x] I tried using the latest stable version of tsc. https://www.npmjs.com/package/typescript
  • [x] I have a question that is inappropriate for StackOverflow. (Please ask any appropriate questions there).
  • [x] [Mention](https://github.com/blog/821-mention-somebody-they-re-notified) the authors (see Definitions by: in index.d.ts) so they can respond.

    • Authors: @Asana, @AssureSign, @Microsoft, @johnnyreilly, @bbenezech, @pzavolinsky, @digiguru, @ericanderson, @morcerf, @tkrotoff, @DovydasNavickas, @onigoetz, @theruther4d, @guilhermehubner, @ferdaber, @jrakotoharisoa, @pascaloliv, @Hotell, @franklixuefei

Right now I am unable to make function that useEffect takes in as async, since it returns following error:

[ts]
Argument of type '() => Promise' is not assignable to parameter of type 'EffectCallback'.
Type 'Promise' is not assignable to type 'void | (() => void)'.
Type 'Promise' is not assignable to type '() => void'.
Type 'Promise' provides no match for the signature '(): void'. [2345]

Example

useEffect(async () => {
  await ...
})

Judging by what I've googled async function inside useEffect is a valid option?

Most helpful comment

Instead of this

const doStuff = async () => {...}

useEffect( () => doStuff())

try this:

const doStuff = async () => {...}
useEffect( () => { doStuff() } )

I had the same issue and it helped.

All 13 comments

Hi! You can now easily use the async await syntax with the React useEffect hook with this package. I hope this will help you, and others.

Cheers :tada: !

@rauldeheer thanks for the package but I think it would be better if we could use async without add extra dependencies. Anyways I'll use your package for now.

Why is this closed? Having issues with this too.

@daryl function within useEffect should not be async by design in react. You either need to implement this logic or use package mentioned by @rauldeheer.

So there are no issues with types, they are actually helping you to catch this incorrect use case.

for anyone else finding themselves here:

const [thing, setThing] = useState(null);

useEffect(() => {
  Api.makeRequest()
    .then(res => {
      // do what you need to do with res here
      setThing(res);
    });
});

A very useful custom hook:

function useAsyncEffect(effect: () => Promise<void | (() => void)>, dependencies?: any[]) {
  return useEffect(() => {
    const cleanupPromise = effect()
    return () => { cleanupPromise.then(cleanup => cleanup && cleanup()) }
  }, dependencies)
}

Instead of this

const doStuff = async () => {...}

useEffect( () => doStuff())

try this:

const doStuff = async () => {...}
useEffect( () => { doStuff() } )

I had the same issue and it helped.

Instead of this

useEffect(async () => {
  await ...
})

try this:

useEffect( {async () => {
  await ...
}} )

I had the same issue and it helped.

This is like your providing object to the useEffect function, BTW I think this is wrong. Objects I think are not represented in this way.

@SanthoshRaju91 I've updated my post. Initially, I put the curly braces in the wrong place. Thanks for noticing.

Let me try this to fix my issue. Thank you for helping us understand the usage of useEffect

A very useful custom hook:

function useAsyncEffect(effect: () => Promise<void | (() => void)>, dependencies?: any[]) {
  return useEffect(() => {
    const cleanupPromise = effect()
    return () => { cleanupPromise.then(cleanup => cleanup && cleanup()) }
  }, dependencies)
}

The cancellation pattern is wrong in my oppinion. It should work as a cancellation mechanism, not deferred cleanup. Otherwise the effect callback would still have chance to have effect on outside state after the hook was already cancelled.

It should be something like this instead:

function useAsyncEffect(effect: (isCanceled: () => boolean) => Promise<void>, dependencies?: any[]) {
  return useEffect(() => {
    let canceled = false;
    effect(() => canceled);
    return () => { canceled = true; }
  }, dependencies)
}

Example:

useAsyncEffect(async (isCanceled) => {
  const result = await doSomeAsyncStuff(stuffId);
  if (!isCanceled()) {
    // TODO: Still OK to do some effect, useEffect hasn't been canceled yet.
  }
}, [stuffId]);

@kostrse How can I use the data returned from "doSomeAsyncStuff" the variable "result" is void.

Hi! You can now easily use the async await syntax with the React useEffect hook with this package. I hope this will help you, and others.

Cheers !

Thanks!!!

Was this page helpful?
0 / 5 - 0 ratings