React

[ReacTS] React Query를 이용한 API 통신(useQuery, useQueries)

cob 2022. 8. 25. 15:02
React Query는 API 통신, 캐싱 등 비동기 과정을 더욱 편하게 하는 데 사용된다.

 

 

** 장점 **
1) fetcher 함수를 만들어 연결시키고, 응답 데이터를 받아온다. ( API 통신 )
2) Caching 매커니즘을 가지고 있어 고유의 key값을 넘기게 되면  API 통신을 하지 않고, 캐시에 저장되어 있던
    데이터를 가져온다. (다시 불러올 경우 캐시에서 가져옴)
3) ReactQueryDevTools 라는 컴포넌트를 가지고 있는데 캐시에 어떤 query가 있는지 보여주고, Data Explorer를 보여준다.

 

 

1. 라이브러리 설치

npm i react-query

 

 


2.  useQuery 사용방법

2-1) Provider 패턴 적용

( index.tsx )

import React from "react";
import ReactDOM from "react-dom/client";
import { QueryClient, QueryClientProvider } from "react-query";
import App from "./App";

/* Client 생성 */
const queryClient = new QueryClient();

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
   // 'App'을 통해 Client 제공  
    <QueryClientProvider client={queryClient}>
        <App />
    </QueryClientProvider>
  </React.StrictMode>
);
  • <QueryClientProvider > 내부에 있는 모든 컴포넌트들은 queryClient에 접근 가능하다.
  • <App /> 둘러싼다. 

 

 

2-2) API 파일 생성

( api.ts )

API 통신을 위한 fetcher함수들을 모아 놓는 파일을 생성
const BASE_URL = `https://api.coinpaprika.com/v1`;

export async function fetcherCoins() {
  return fetch(`${BASE_URL}/coins`).then((response) => response.json());
}

/* parameter 값이 필요할 경우 */
export function fetchCoinInfo(coinId: string) {
  return fetch(`${BASE_URL}/coins/${coinId}`).then((response) =>
    response.json()
  );
}

 

 

2-3)  API 통신 (useQuery Hook 사용)

/* useQuery Hook 사용방법 */
const {isLoading, status, data} = useQuery("key", "fetcher 함수", {"옵션"})
  • 첫 번째 param : query Key로 query의 고유 식별자
  • 두 번째 param : fetcher 함수 (API 통신)
  • 세 번째 param : 선택적 Object 여러가지 useQuery 옵션 사용가능.
  • isLoading : boolean 값을 리턴한다. (불러오는 중 = false, 불러옴 = true )
  • data : 응답받은 데이터가 담겨있다.
  • status: error/success/loading 등 string으로 반환되며 한번에 처리 할 수 있다.
옵션  
refetchInterval  지정한 시간에 따라 재요청 한다.
staleTime -  fresh => stale 상태로 변경되는데 걸리는 시간
- 데이터가 응답받은 이후 staleTime이 지나지 않았다면, unmount 후 mount 되어도 다시 요청하지 않는다.
cacheTime - 데이터가 비활성화 상태일때 캐싱된 상태로 남아있는 시간
- cacheTime은 staleTime과 관계없이, 무조건 비활성화된 시점을 기준으로 캐시 데이터 삭제를 결정한다
refetchOnWindowFocus 윈도우가 다른 곳을 갔다가 다시 화면으로 돌아오면 이 함수를 재실행합니다. 그 재실행 여부 옵션 입니다.(true/false)
retry 요청 실패시 재요청 횟수
enabled  useQuery를 동기적으로 사용가능하다. 값이 true가 되면 fetcher함수가 실행된다.
onSuccess: data => { console.log(data); } 성공시 호출
onError:e => { console.log(e) } 실패시 호출, 401, 404같은 오류가 아니라 api 호출이 실패한 경우만 호출된다.

 

/* Parameter가 있는경우 */

interface InfoData {
  id: string
  name: string;
}
interface RouteParams {
  coinId: string;
}

// URL로 넘어온 param
const { coinId } = useParams<keyof RouteParams>() as RouteParams;
const { isLoading, data } = useQuery<InfoData>(
	"key",
    () => fetchCoinInfo(coinId), // 람다식
    { refetchInterval: 5000 }
 );
  • fetcher함수에 파라미터 값을 줘야 할 경우 바로 실행할 수 있는 람다식(익명 함수)을 사용한다.
    - 파라미터 없는 경우 : fetchCoinInfo
    - 파라미터 있는 경우 : ()  =>  fetchCoinInfo(cionId)
  • refetchInterval : refetch 시간을 5초로 설정 (백그라운드를 통해 주기적으로 앱을 업데이트 해준다.)

 

/* 2개의 useQuery Hook을 가질 경우 */

const { isLoading: infoLoading, data: infoData } = useQuery<InfoData>(
    ["info", coinId],     // Array로 변경
    () => fetchCoinInfo(coinId),
    { refetchInterval: 5000 }
  );
const { isLoading: tickersLoading, data: tickersData } = useQuery<PriceData>(
    ["tickers", coinId],
    () => fetchCoinTickers(coinId),
    { refetchInterval: 5000 }
  );
  • ["고유 값", 동일한 값] : key를 Array 변경하여 첫 번째 값을 고유 값으로 카테고리 역할을 하도록 하고, 두 번째 값을 중복되는 값을 줘서 고유한 부분이 되도록 한다.
  • isLoading, data가 중복되기 때문에 이름을 바꾼다.

 

 

( App.tsx ) 기본 useQuery Hook 적용

import { useQuery } from "react-query";

interface ICoin {
  id: string;
  name: string;
}

function App() {
  const { isLoading, data } = useQuery<ICoin[]>("allCoins", fetcherCoins);

  return (
    <>
      {isLoading ? (
        "Loading..."
      ) : (
        <ul>
          {data?.slice(0, 100).map((coin) => (
            <li key={coin.id}>{coin.name}</li>
          ))}
        </ul>
      )}
    </>
  );
}
export default App;

 

 

 


3.   useQueries사용방법

useQuery를 비동기로 여러개 실행할 경우 변수에 대한 로딩, 성공, 실패처리를 모두 해야한다.
const usersQuery = useQuery("users", fetchUsers);
const teamsQuery = useQuery("teams", fetchTeams);
const projectsQuery = useQuery("projects", fetchProjects);

 

 

3-1)  useQueries를 사용하여 하나로 묶는 방법

const result = useQueries([
  {
    queryKey: ["getRune", riot.version],
    queryFn: () => api.getRunInfo(riot.version)
  },
  {
    queryKey: ["getSpell", riot.version],
    queryFn: () => api.getSpellInfo(riot.version)
  }
]);

useEffect(() => {
  console.log(result); // [{rune 정보, data: [], isSucces: true ...}, {spell 정보, data: [], isSucces: true ...}]
  const loadingFinishAll = result.some(result => result.isLoading);
  console.log(loadingFinishAll); // loadingFinishAll이 false이면 최종 완료
}, [result]);

 

 

3-2 useQueries TypeScript

 const results = useQueries({
    queries: [
      { queryKey:  ["getRune", riot.version], queryFn: () => api.getRunInfo(riot.version), staleTime: Infinity},
      { queryKey: ["getSpell", riot.version], queryFn: () => api.getSpellInfo(riot.version), staleTime: Infinity}
    ]
  });

4.  DevTools 컴포넌트 적용

캐시에 있는 query를 볼 수 있다

 

( App.tsx )

import { ReactQueryDevtools } from "react-query/devtools";

function App() {
  return (
    <>
      ...
      <ReactQueryDevtools initialIsOpen={true} />
    </>
  );
}

export default App;
  • <ReactQueryDevtools /> : DevTools 컴포넌트를 맨 아래 영역에 위치시킨다.

 

 

 

반응형