Joseph
Joseph

Reputation: 73

Async/Await function gets called twice

I made a simple Async function and wanted to test run it in my react app. The function is:

async function WAIT(time: any) {
  await new Promise((r) => setTimeout(r, time))
}

export async function GetMemberList(orgId: string):Promise<ResponseInterface>{
  await WAIT(3000);
  return {code: 404, data: "Data"}
} 

And i try to call it in my main app like this:

function App() {
  async function fn() {
    let x = await GetMemberList('SOme');
    console.log(x);
  }
  fn();
  return (<div>.....<div>)
}

But the problem is console.log(x) runs twice. I get {code: 404, data: "Data"} twice. Why is that? Thanks

Upvotes: 0

Views: 5360

Answers (3)

G&#248;ran Cantona
G&#248;ran Cantona

Reputation: 641

By calling fn() directly in your component, it will trigger every time your component renders. This can happen for many reasons.

You can avoid this with useEffect, and specify the dependencies of the function. In this case, it's only the function itself.

function App() {
  async function fn() {
    let x = await GetMemberList('SOme');
    console.log(x);
  }
  
  useEffect(() => {
    fn();
  }, [fn]);
  return (<div>.....<div>)

However, since fn is redefined every time the component renders, it would still be called every time.

In this specific case, you should define the function outside of the component. It doesn't depend on any data provided by the component, so it's basically a static function. It will never change. In this case, you can safely remove fn from the dependency array

async function fn() {
  let x = await GetMemberList('SOme');
  console.log(x);
}

function App() {
  useEffect(() => {
    fn();
  }, []);
  return (<div>.....<div>)

There are other ways to do this too. You can define the fn function inside the useEffect, and you can define it with useCallback, so that it will have a stable reference between renders.

function App() {
  useEffect(() => {
    async function fn() {
      let x = await GetMemberList('SOme');
      console.log(x);
    }
    fn();
  }, []);
  return (<div>.....<div>)

With useCallback:

function App() {
  const cb = useCallback(async() => {
    let x = await GetMemberList('SOme');
    console.log(x);
  }, []);

  useEffect(() => {
    cb();
  }, [cb]);
  return (<div>.....<div>)

Upvotes: 0

Barry Michael Doyle
Barry Michael Doyle

Reputation: 10658

Here's the solution:

function App() {
  async function fn() {
    let x = await GetMemberList('SOme');
    console.log(x);
  }

  React.useEffect(() => {
    fn();
  }, []);

  return (<div>.....<div>)
}

Making use of useEffect with an empty dependency array will make it so that the fn() only gets called once upon the App component mounting.

Upvotes: 2

Rehan Sattar
Rehan Sattar

Reputation: 4005

I tried running your code and it's outputting once.

Most probably, the problem is that something is triggering a rerender in your template and the fn() is getting called again.

You can easily verify it by wrapping your fn() inside of a useEffect().

Upvotes: 1

Related Questions