React Query를 사용하기 위해 우선 라이브러리를 설치한다.
npm install react-query
쿼리 클라이언트를 생성한다.
쿼리 클라이언트는 쿼리와 서버의 데이터 캐시를 관리하는 클라이언트이다.
const queryClient = new QueryClient();
마지막으로 자녀 컴포넌트에 캐시와 클라이언트 구성을 제공할 QueryClientProvider를 적용한다.
queryClient가 가지고 있는 캐시와 모든 기본 옵션을 QueryClientProvider의 자녀 컴포넌트도 사용할 수 있게된다.
<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훅을 사용해보자.
api는 JSONPlaceholder를 사용했다.
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를 뜻한다.
isLoading
과 isError
는 queryFn을 실행하는동안 로딩되고 있거나 에러가 발생할 경우의 여부에 따라 boolean값으로 받아올 수 있다.
const {data, isLoading, isError} = useQuery<IPost[]>('posts', fetchPosts);
if(isError) return (<h3>ERROR!</h3>)
if(isLoading) return (<h3>Loading...</h3>)
return ( ...
isFetching
: 비동기쿼리가 해결되지 않았음. 아직 Fetching이 완료되지 않음
isLoading
: 쿼리함수가 해결되지 않았고, 캐시된 데이터도 없다
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을 줄일 수 있다
stale
: refetcing할 때의 고려사항.
cache
: 나중에 다시 필요할 수 있는 데이터.
캐시가 만료되면 가비지 컬렉션이 실행되어 클라이언트는 해당 데이터를 사용할 수 없다.
데이터가 캐시에 있는 동안 fetching할 때 사용 가능하다.
참고자료 및 이미지 출처: https://www.udemy.com/course/react-query-react/