5

I know its not recommended to create add async to useEffect but how can I make sure my function is completely done before continuing

Here is my code

useEffect(
   () => {
         const RetrieverDataProcess = async () => {
             const GetMainListResults = await GetMainList(FORMSTATUS.ID);
             setMAINOBJECT(GetMainListResults); //useState
             console.log(2)
         }

         console.log(1);
         RetrieverDataProcess();
         console.log(99);

    }, [])

If I run in my console its showing

1

99

2

I was hopeing its

1

2

99

Matthias
  • 13,607
  • 9
  • 44
  • 60
Hawk
  • 514
  • 1
  • 7
  • 22
  • Possible duplicate of [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Emile Bergeron Nov 28 '19 at 20:07

4 Answers4

4

Are you doing this because you want the MAINOBJECT state property to be set by the time you're ready to execute console.log(99)?

If so, one thing you could do is use an additional useEffect hook that only executes when the state property MAINOBJECT updates:

useEffect(() => {
  if (MAINOBJECT) {
    console.log(99);
  }
}, [MAINOBJECT]);
shawnyates
  • 167
  • 2
  • What I want to achieve was after RetrieverDataProcess function is completed, I will turn off my loading screen. For now I just added console log to make it simple. – Hawk Nov 28 '19 at 19:43
  • Since you're setting the state property MAINOBJECT at the end of RetrieverDataProcess, the solution I provided should work. Try to execute your loading screen logic within the additional hook I suggested. – shawnyates Nov 28 '19 at 19:48
4

Can you try this (not at pc right now)

useEffect(
   () => {
         const RetrieverDataProcess = async () => {
             const GetMainListResults = await GetMainList(FORMSTATUS.ID);
             setMAINOBJECT(GetMainListResults); //useState
             return console.log(2)
         }

         console.log(1);
         RetrieverDataProcess().then(()=>{
             console.log(99);
         });


    }, [])
Renaldo Balaj
  • 2,390
  • 11
  • 23
  • I am ok using then but doesnt it breaking the async/await idea. – Hawk Nov 28 '19 at 20:09
  • if i would be you i would put console.log(99) inside the function(after console2), :P but you wanted it like this – Renaldo Balaj Nov 28 '19 at 20:10
  • 2
    The async/await concept only applies within the async function. Outside of it, you are back to Promises. @giorgim had the right explaination, but he has deleted his answer. – Richard Matsen Nov 29 '19 at 01:14
3

You may use an Immediately Invoked Function Expression or IFFY. This will also allow you to use async/await instead of going back to then-chaining.

Essentially you are wrapping the effect body in an async function, which you immediately invoke.

useEffect(() => {
     (async function anyName() {
         const RetrieverDataProcess = async () => {
             const GetMainListResults = await GetMainList(FORMSTATUS.ID);
             setMAINOBJECT(GetMainListResults); //useState
             console.log(2);
         }

         console.log(1);
         // now you can await the async function to make sure it completes
         await RetrieverDataProcess();
         console.log(99);
     // note the invocation:
     })();
}, [])
Bastian Stein
  • 2,147
  • 1
  • 13
  • 28
1

Just put your logic outside the EFFECT.

You can define an async function in component and then call it in EFFECT. If you care the performance, wrap it with useCallback. Don't forget setting it as a dependency.

The better practice is to extract the static logic into small functions outside the component.

Singhi John
  • 307
  • 2
  • 10