key가 동일한 useQuery 두 번 사용하기

김준엽·2023년 3월 22일
0

react-query

목록 보기
1/1

react-query v3.39.2 기준

목차

  • 어디서 두 번 사용했어?
  • 두 번 사용한 이유
  • 두 번 사용해서 발생한 문제
  • 해결방법
  • 결론

어디서 두 번 사용했어?

부모 컴포넌트와 자식의 자식 컴포넌트에서 사용했다.

두 번 사용한 이유?

부모 컴포넌트에서 useQuery로 필요한 데이터를 받고 자식의 자식 컴포넌트에서 그 데이터가 필요했다. 세 가지 방법이 생각났다.

  • props로 넘겨주기
  • 전역상태관리 라이브러리 recoil 이용하기
  • 똑같은 useQuery를 자식의 자식 컴포넌트에서 사용하기

첫 번째 방법은 depth가 2개라서 번거롭다. 두 번째 방법은 atom을 만들고 상태값을 set해줘야 하기 때문에 번거롭다. 마지막 방법은 애초에 캐싱을 한고 그걸 이용한다면 리소스 낭비가 적을 것 같고 컴포넌트 관리측면에서 위의 두 가지 방법보다 좋다. 그래서 두 번 사용하기로 했다.

두 번 사용해서 발생한 문제

useQuery에서 fetch가 성공하는 경우에는 문제가 없었다. 그러나 실패한 경우 문제가 발생했다. 무한 렌더링이 발생했다.

아래는 문제를 발생시킨 간단한 예제다. 부모 컴포넌트와 자식 컴포넌트에서 useQuery를 사용했다. 실무에서 작성한 코드는 부모와 자식의 자식이지만 문제가 발생하는 원인은 같아 좀 더 이해하기 쉬운 예제로 작성했다.

문제의 원인은 에러가 나면 isLoading 상태를 저장하지 않기 때문이다. 이거와 맞물려 부모 컴포넌트에서 로딩처리하는 부분이 있으면 무한 렌더링이 발생한다.

  1. 부모 컴포넌트 : isLoading - true → 로딩 표시
  2. 부모 컴포넌트 : useQuery 에러 발생
  3. 부모 컴포넌트 : isLoading - false → 자식 컴포넌트 렌더링
  4. 자식 컴포넌트 : isLoading - true

이 4번재 순간에 자식 컴포넌트에서 isLoadingtrue가 되면 부모 컴포넌트에 있는 isLoadingtrue로 갱신된다. 그러면 다시 위의 과정 반복이 일어난다. 부모 컴포넌트에서 에러가 발생하고 isLoadingfalse가 되면 자식 컴포넌트가 또 렌더링 되서 자식 컴포넌트 isLoadingtrue가 된다. 여기서 무한 굴레에 빠지게 된다.

해결방법

해결방법은 두가지가 있다.

  • 부모 컴포넌트에서 에러가 발생하면 자식 컴포넌트 렌더링을 막는다.(비추천)
import Child from "./Child";
import useCustomQuery from "./useCustomQuery";

export default function Parent() {
  const { data, isLoading, isError } = useCustomQuery();
  console.log("Parent", isLoading);
  console.log(isError);

  if (isLoading) return <div>Loading...</div>;
  if (isError) return null; // 에러처리
  return (
    <div>
      <h1>Parent</h1>
      <Child />
    </div>
  );
}

이 방법은 useQuery에 onError로 에러 처리를 할 경우 onError 에러 처리와 isError로 에러 처리를 해야 하므로 두 번 신경써야 하기 때문에 좋지 않은 방법이다.

  • useQueryClient 사용하기(추천)
import useCustomQuery from "./useCustomQuery";

export default function Child() {
	const queryClient = useQueryClient();
  const data = queryClient.getQueryData("getData");

  return (
    <div>
      <h1>Child</h1>
    </div>
  );
}

useQueryClient를 이용하면 캐싱된 데이터를 불러올 수 있다. useQuery를 두 번 호출하지 않는 방법이다. 깔끔해서 좋다.

결론

useQueryisLoading은 최초로 fetch가 되면 일반적으로 변하지 않는다. 한 번 true → false로 되면 다음에는 똑같은 useQuery를 호출하면 isLoadingfalse이고 isFetchingtrue → false로 변한다. cacheTime: 0를 설정하거나 queryClient.invalidateQueries(key) 사용해도 마찬가지다. 얼마동안 시간만큼 isLoading 상태를 캐싱하는 지는 모르겠다.

최초로 useQuery를 호출하고 그 뒤에 똑같은 useQuery를 호출했을 때 isLoading이 변하는 경우는 두 가지다. fetch할 때 에러가 난 경우queryClient.clear()를 실행한 경우다. 다만, queryClient.clear()는 모든 캐시를 지우기 때문에 주의가 필요하다.

profile
프론트엔드 개발자

0개의 댓글