개발노트/React

React Hook "useEffect" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks

Dahoon06 2023. 6. 6. 14:59
728x90
반응형

 

랜덤 레시피를 불어와 화면에 보여주는 컴포넌트 내부에 있던 react-query 코드를 분리하는 중에 에러가 발생했다.

 

원인은 Hook을 불러오는 과정에 있었다.

 

리액트에서 Hook을 사용할때 지켜야할 조건이있다.

 

1. 리액트 함수 내에서만 사용되어야한다. (일반적인 js 내에서는 호출 불가능)
2. 리액트 함수 최상 위에서 호출해야한다. (반복문, 조건문, 중첩 함수 내에서 호출 불가능)

useState 나 useEffect 같은 Hook 들은 여러번 사용될 수 있는데, 이 Hook들은 호출되는 순서대로 저장해 놓는다.(LinkedList)

( 그래서 매 렌더링마다 순서대로 Hook을 호출할 수 있는것 )

 

항상 리액트 함수 최상위 레벨에서만 Hook을 호출해야하는 이유

근데 조건문이나 반목문 안에서 Hook을 사용하게 되면 어떤 조건에 따라 Hook이 실행되지 않게 되고, 그러면 렌더링 때마다 맨처음 함수가 실행될 때 리액트에 저장해둔 Hook의 호출 순서가 꼬여 에러가 발생한다.

 

리액트 함수 내에서만 사용되어야하는 이유

여러 상태값을 사용하기 위해 useState를 여러번 사용할 수 있다. 이렇게 생성된 여러 상태값들을 관리해주기 위해 리액트는 useState 함수 바깥에 state를 배열에 담아서 저장해 놓는다.

즉, 상태 값들은 이 배열 순서대로 저장되고, 상태가 업데이트 됐을 때 이 상태들은 리액트 컴포넌트 바깥에 선언되어 있는 변수들이기 때문에 업데이트 이후에도 이 변수에 접근이 가능하다.

생성된 상태들이 컴포넌트를 키로 하는 배열에 순서대로 저장되있기 때문에 Hook을 조건문이나 반복문, 일반 js 함수에서 사용되게 된다면, 최초에 저장되어있던 순서와 다르게 실행되어 다른 상태값을 참조하게 되는 에러를 발생시킬 수 있다.

 

[문제 코드]

 

useEffect(() => {
  queryClient.prefetchQuery(recipeKeys.getRandomRecipe, getRandomRecipe)
}, [queryClient])

export function useRandomRecipe(page: number): IRecipe.Card[] {
  const { data = [] } = useQuery(
    [recipeKeys.getRandomRecipe, page],
    getRandomRecipe,
    {
      refetchOnMount: true,
    }
  )
  return data
}

 

컴포넌트에서 사용된 함수를 그대로 옮기다 보니 prefetching을 위해 선언했던 useEffect의 위치가 잘못되었다.

React Hook을 사용하기 위해서는 리액트 함수 내에서 선언해야하는데 나의 경우는 단순 ts 위치에서 Hook을 불러오게된 경우

 

Hook 호출 조건에 따라 리액트 함수 내에서 useEffect를 사용하도록 수정

 

export function useRandomRecipe(page: number): IRecipe.Card[] {
  useEffect(() => {
    queryClient.prefetchQuery(recipeKeys.getRandomRecipe, getRandomRecipe)
  }, [queryClient, page])

  const { data = [] } = useQuery(
    [recipeKeys.getRandomRecipe, page],
    getRandomRecipe,
    {
      refetchOnMount: true,
    }
  )
  return data
}

 

728x90
반응형