[React-Query] 시작하기

상민·2022년 7월 11일
1

React Query

목록 보기
2/6
post-thumbnail

React Query 시작하기

React Query를 사용하기 위해 우선 라이브러리를 설치한다.

npm install react-query

쿼리 클라이언트를 생성한다.
쿼리 클라이언트는 쿼리와 서버의 데이터 캐시를 관리하는 클라이언트이다.

const queryClient = new QueryClient();

마지막으로 자녀 컴포넌트에 캐시와 클라이언트 구성을 제공할 QueryClientProvider를 적용한다.
queryClient가 가지고 있는 캐시와 모든 기본 옵션을 QueryClientProvider의 자녀 컴포넌트도 사용할 수 있게된다.

  • src/index.tsx

<import React from 'react';
import ReactDOM from 'react-dom/client';
import { QueryClient, QueryClientProvider } from 'react-query';
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root')!)
const queryClient = new QueryClient();

root.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </React.StrictMode>
);

useQuery훅 사용하기

이제 서버에서 데이터를 가져오기 위해 useQuery훅을 사용해보자.
api는 JSONPlaceholder를 사용했다.

  • src/Posts.tsx

import { useState } from "react";
import { useQuery } from 'react-query';
import { PostDetail } from "./PostDetail";

export interface IPost {
  userId: number;
  id: number;
  title: string;
  body: string;
}

async function fetchPosts() {
  const response = await fetch(
    "https://jsonplaceholder.typicode.com/posts?_limit=10&_page=0"
  );
  return response.json();
}

function Posts() {
  const [currentPage, setCurrentPage] = useState(0);
  const [selectedPost, setSelectedPost] = useState<IPost | null>(null);

  // replace with useQuery
  const {data, isLoading} = useQuery<IPost[]>('posts', fetchPosts);

  if(isLoading) return <div/>
  return (
    <>
      <ul>
        {data?.map((post) => (
          <li
            key={post.id}
            className="post-title"
            onClick={() => setSelectedPost(post)}
          >
            {post.title}
          </li>
        ))}
      </ul>
      <div className="pages">
        <button disabled onClick={() => {}}>
          Previous page
        </button>
        <span>Page {currentPage + 1}</span>
        <button disabled onClick={() => {}}>
          Next page
        </button>
      </div>
      <hr />
      {selectedPost && <PostDetail post={selectedPost} />}
    </>
  );
}

export default Posts

useQuery훅에서 받는 첫 번째 인수는 QueryKey이다.
쿼리의 이름을 말하는 것인데 'posts'라고 입력했다.

두 번째 인수는 QueryFunction이다.
데이터를 받아오는 비동기 함수여야 하는데 정의한 fetchPosts 함수를 사용했다.

const {
   data,
   dataUpdatedAt,
   error,
   errorUpdatedAt,
   failureCount,
   isError,
   isFetched,
   isFetchedAfterMount,
   isFetching,
   isIdle,
   isLoading,
   isLoadingError,
   isPlaceholderData,
   isPreviousData,
   isRefetchError,
   isRefetching,
   isStale,
   isSuccess,
   refetch,
   remove,
   status,
 } = useQuery(queryKey, queryFn?, {
   cacheTime,
   enabled,
   initialData,
   initialDataUpdatedAt
   isDataEqual,
   keepPreviousData,
   meta,
   notifyOnChangeProps,
   notifyOnChangePropsExclusions,
   onError,
   onSettled,
   onSuccess,
   placeholderData,
   queryKeyHashFn,
   refetchInterval,
   refetchIntervalInBackground,
   refetchOnMount,
   refetchOnReconnect,
   refetchOnWindowFocus,
   retry,
   retryOnMount,
   retryDelay,
   select,
   staleTime,
   structuralSharing,
   suspense,
   useErrorBoundary,
 })
 
 // or using the object syntax
 
 const result = useQuery({
   queryKey,
   queryFn,
   enabled,
 })

useQuery에서 반환되는 객체는 여러가지가 있다.
이중에서 위 코드에서 사용한 data는 fetchPosts에서 반환되는 data를 뜻한다.
isLoadingisError는 queryFn을 실행하는동안 로딩되고 있거나 에러가 발생할 경우의 여부에 따라 boolean값으로 받아올 수 있다.

const {data, isLoading, isError} = useQuery<IPost[]>('posts', fetchPosts);

  if(isError) return (<h3>ERROR!</h3>)
  if(isLoading) return (<h3>Loading...</h3>)
  return ( ...

isLoading vs isFetching

isFetching: 비동기쿼리가 해결되지 않았음. 아직 Fetching이 완료되지 않음
isLoading: 쿼리함수가 해결되지 않았고, 캐시된 데이터도 없다


ReactQueryDevtools 사용하기

ReactQueryDevtools는 쿼리키로 쿼리를 표시해주고 활성(active), 비활성(inactive), 만료(stale) 등 모든 쿼리의 상태를 알려준다.
쿼리에 의해 반환된 데이터를 확인할 수 있는 데이터 탐색기도 있고 쿼리를 볼 수 있는 쿼리 탐색기도 있다.

import React from 'react';
import ReactDOM from 'react-dom/client';
import { QueryClient, QueryClientProvider } from 'react-query';
import {ReactQueryDevtools} from 'react-query/devtools'; // 추가
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root')!)
const queryClient = new QueryClient();

root.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
      <ReactQueryDevtools/> // 추가
    </QueryClientProvider>
  </React.StrictMode>
);

npm start로 리액트 서버를 실행하고 브라우저를 확인해보면 브라우저 왼쪽아래에 리액트쿼리 로고모양 버튼이 생긴다. 이를 클릭하면 Devtools를 사용할 수 있다.

Devtools에서 볼 때 "posts"는 stale상태인 것을 알 수 있다.

stale은 데이터가 만료되었음을 의미한다.

데이터가 stale상태면 데이터가 만료되었으므로 React Query는 해당 데이터를 refetching을 한다.

staleTime은 Default값으로 0인데, useQuery의 옵션에서 staleTime을 직접 지정해줄 수 있다.

const {data, isLoading, isError} = useQuery<IPost[]>('posts', fetchPosts, {staleTime: 2000});

staleTime을 2초로 설정해 두었기 때문에 2초동안은 fresh상태가 지속된다.

refetcing이 실행될 경우라도 데이터가 stale상태가 아니라면 refetching이 일어나지 않는다.

staleTime을 지정해줌으로써 무분별한 data fetching을 줄일 수 있다


staleTime vs cacheTime

stale: refetcing할 때의 고려사항.
cache: 나중에 다시 필요할 수 있는 데이터.

캐시가 만료되면 가비지 컬렉션이 실행되어 클라이언트는 해당 데이터를 사용할 수 없다.
데이터가 캐시에 있는 동안 fetching할 때 사용 가능하다.

참고자료 및 이미지 출처: https://www.udemy.com/course/react-query-react/

profile
FE Developer

0개의 댓글