2

I need to fetch an api in an useEffect(). I tried to do it and it throws me the following error: Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

My code is the following:

-Api I need to fetch:

export const fetchMyApi = async (code) => {
  const { data } = await useQuery("data", () =>
    clientAPIs.getData(code)
  );
  return data;
};

-My useEffect code:

  useEffect(() => {
    await fetchMyApi(values.code)
  }, [values]);

I have found many issues about this on internet but they all offer basically the same solution such as:

  useEffect(() => {
    fetchMyApi(values.code)
      .then((data) => {
        return data?.options;
      })
      .then((data) => {
        console.log(data);
      })
      .catch((err) => {
        console.log(err);
      });
  }, [values]);

or

  useEffect(() => {
    let didCancel = false;
    async function fetch() {
      const result = await fetchMyApi(values.code);
      if (!didCancel) {
        console.log(result);
      }
    }
    fetch();
    return () => {
      didCancel = true;
    }; 
  }, [values]);

None of those works in my case. I am using Nextjs framework.

What should I try?

Raf Dor
  • 145
  • 2
  • 11
  • I am using this hook for all my async logic in React: https://github.com/corets/use-async – M K Jul 22 '21 at 17:40
  • This is a duplicate https://stackoverflow.com/questions/53332321/react-hook-warnings-for-async-function-in-useeffect-useeffect-function-must-ret – dir Jul 22 '21 at 17:44
  • It sounds like you might be calling a hook outside the body of a function component. – Chris Farmer Jul 22 '21 at 17:47
  • @dir have you read the question? that solution is the last I proposed but it does not work – Raf Dor Jul 22 '21 at 17:47
  • Yes, I have read the question, there are many solutions on my linked question and you are certainly not the first person to ask "how can I use async in my useEffect" – dir Jul 22 '21 at 17:48
  • 2
    If you remove `await` from `await fetchMyApi(values.code)` then your code should work fine. You can _call_ async code from an effect, but an effect cannot return a promise, which means you cannot use await in the synchronous flow of an effect. – Alex Wayne Jul 22 '21 at 17:49
  • @AlexWayne Thank you man, but I have already tried and it throws me the same error (unhandled in this case). Keep in mind that `clientAPIs.getData(code)` is an async method that calls an api with *code* as parameter. same error with `try-catch`.. – Raf Dor Jul 22 '21 at 18:18
  • 2
    Also, you cannot call a hook from an effect. What is `useQuery` here? That seems like a hook that calls other hooks. And if so it _must_ be called from the main functional component body and not from an effect. Looking at the `useAsync` hook you linked, it returns a bunch of functions to manage the async operations. So you'll want to call your hook at the root level, and then call those returned function from your effects. – Alex Wayne Jul 22 '21 at 18:29
  • useQuery is from `react-query` – Raf Dor Jul 22 '21 at 19:00

1 Answers1

2

Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

You are breaking the rules of hooks. Hooks are only valid when called while synchronously rendering a React component. An effect runs separately after a componenent renders. So you cannot call a hook from an effect.

And yes you are calling a hook from an effect because this effect:

  useEffect(() => {
    await fetchMyApi(values.code)
  }, [values]);

Calls the fetchMyApi function, which calls a hook:

await useQuery('data', () => /* ... */)

You are using react query incorrectly.

First make your fetchMyApi just do it's async task, and nothing else:

export const fetchMyApi = (code) => {
  return clientAPIs.getData(code)
};

Then call useQuery from the root of your functional component:

function MyComponent() {
  const { data } = useQuery('data', () => fetchMyApi('some code here'))
}

The documentation should take you the rest of the way from here

Alex Wayne
  • 178,991
  • 47
  • 309
  • 337