Problem with using React custom Hook in useEffect

63 views Asked by At

I made React custom hook with react-query but had problem with using it in components

this is custom hook, @param : code @return : id Token

export const useGetIdToken = (code: string | null) => {
  const logintype = "kakao";

  const { isLoading, data, error } = useQuery({
    queryKey: ["idToken"],
    queryFn: async () => {
      const res = await axiosInstance.get(
        `/v1/auth/${logintype}/idtoken?code=${code}`,
      );
      return res.data;
    },
  });

  return {
    idToken: data || null,
    isLoading,
    error,
  };
};

and using in component like this

const KakaoLogin = () => {
  //path에서 query string 받아오기
  const [searchParams, setSearchParams] = useSearchParams();
  const code = searchParams.get("code");

  //code 백으로 보내고 idToken 받아오기
  const fetchData = useGetIdToken(code);
  const idToken = fetchData.idToken;
  useEffect(() => {
    console.log(idToken);
  }, [idToken]);

  // useEffect(() => {
  //   console.log(code);
  //   const fetchData = useGetIdToken(code);
  //   console.log(fetchData.idToken);
  // }, [code]);

  return <div>Loading...</div>;
};

this works but if i'm using it in useEffect() like comment i got errors 'Invalid hook call' but i want to make this custom hook work only when variable 'code' is changed is any other way i can use it in useEffect?

i made function using custom hook, and call it in useEffect but it stays in same errors. then i just give up using useEffect and put hook at the top then it works successfully but i'm still curious about why these error came out and any other way to fix it

first error comments are following

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
   See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
   at Object.throwInvalidHookError (react-dom.development.js:16227:1)
   at Object.useContext (react.development.js:1618:1)
   at useQueryClient (QueryClientProvider.tsx:11:1)
   at useBaseQuery (useBaseQuery.ts:42:1)
   at useQuery (useQuery.ts:46:1)
   at useGetIdToken (useGetIdToken.tsx:13:1)
   at kakaoLogin.tsx:23:1
   at commitHookEffectListMount (react-dom.development.js:23150:1)
   at commitPassiveMountOnFiber (react-dom.development.js:24926:1)
   at commitPassiveMountEffects_complete (react-dom.development.js:24891:1)

install BrowserRotuer, make functions with hooks and call it in useEffect but never works i hope i can get why custom hook can't be used in useEffect, and any other option i can use it when this first renders

1

There are 1 answers

0
Loretta On

I will break this into two parts:

  1. Debunking the error you experience
  2. Resolving the React Query to track code

"Invalid hook call"

The "Invalid hook call" is a very common issue when working with React and occurs when you don't follow the rules of React hooks.

The error itself is very descriptive of what the issue is and where you can read more about it but let's debunk it here:

React hooks, including custom hooks like your useGetIdToken, must adhere to the Rules of Hooks. These rules include:

  • Only Call Hooks at the Top Level: Don’t call Hooks inside loops, conditions, or nested functions. This also includes useEffect as with your example.
  • Only Call Hooks from React Functions: Call them from within React functional components and not regular JavaScript functions.

In your case the error occurs when you try to use the custom hook inside the useEffect hook. This is a violation of the first rule, as useEffect can be considered a nested function within your component.

To resolve this, you should call your custom hook at the top level of your component, just like you did in your working example.

How to make a React Query Query listen for changes

You can use your criteria as a query key. The query is dependable on the data (code) and when the data is a part of the query key, the query will update any time the data (code in your case) updates.

The query keys act as a dependency of the query. Whenever a query key changes, the query will automatically update.

export const useGetIdToken = (code: string | null) => {
  const logintype = "kakao";

  const { isLoading, data, error } = useQuery({
    queryKey: ["idToken", code], // <---- add code as a query key
    queryFn: async () => {
      const res = await axiosInstance.get(
        `/v1/auth/${logintype}/idtoken?code=${code}`,
      );
      return res.data;
    },
  });

  return {
    idToken: data || null,
    isLoading,
    error,
  };
};

You can read about it in the TanStack documentation about Query Keys.


TLDR; Using any types of hooks in useEffect goes against the React Rules of Hooks.There is always another way to work with hooks that does not include using the hook itself in useEffect. You can see the code above to learn how to make your React Query query listen for changes without using it directly in a useEffect hook.