데이터 페칭 간소화: 서버에서 데이터를 가져오는 코드를 간단하게 작성가능.
자동 캐싱 및 갱신: 데이터를 캐싱하고, 필요에 따라 최신 상태로 자동 갱신.
효율적인 상태 관리: 로딩, 에러, 성공 상태 관리와 같은 반복 작업을 줄이고, 로직을 더 직관적으로 작성할 수 있음.
자동화된 리페칭: 데이터의 유효 기간 설정과 백그라운드 리페칭을 통해 최신 상태를 유지.
전역 데이터 동기화: 같은 데이터를 여러 컴포넌트에서 공유할 때 데이터 동기화를 쉽게 할 수 있음.
캐싱, 백그라운드 리페칭, 데이터 유효성 검사 등의 기능을 통해 서버 데이터를 효율적으로 관리.
캐싱: 데이터를 캐시하여, 동일한 데이터를 여러 컴포넌트에서 사용할 때 서버에 중복 요청을 보내지 않도록 함.
캐시된 데이터는 설정한 유효 기간이 지나면 자동으로 갱신됨.(gcTime
)
쿼리 키(Query Key): 각 데이터 요청에 고유한 쿼리 키를 부여하여 React Query가 요청을 식별하고, 캐시에 저장하거나 갱신 시 사용하는 기준으로 함.
백그라운드 리페칭: 데이터가 오래되거나 유효 기간이 지난 경우, 백그라운드에서 자동으로 서버와 동기화하여 최신 데이터를 유지함.(staleTime
)
옵션 기반 유연성: staleTime
, gcTime
, retry
등의 다양한 옵션을 통해 데이터 유효성 및 갱신 주기를 조정할 수 있어, 특정 데이터의 필요에 맞게 동작을 세부 조정가능.
설치
npm i @tanstack/react-query
React Query Provider 설정: React Query의 기능을 사용하려면 최상위 컴포넌트에 QueryClientProvider를 추가해야 함
index.js
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
root.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
);
npm i @tanstack/react-query-devtools
inedex.js
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
const queryClient = new QueryClient();
root.render(
<QueryClientProvider client={queryClient}>
<App />
{/* initialIsOpen는 devtools을 프로젝트시작할때 연상태로 시작할지, buttonPosition는 devtools 위치정해주기 */}
<ReactQueryDevtools initialIsOpen={false} buttonPosition="bottom-left" />
</QueryClientProvider>
);
npx json-server db.json
queryKey
queryKey
를 통해 쿼리를 캐싱하고, 동일한 데이터를 재사용할지 여부를 판단.['posts']
), 또는 고유 식별이 필요할 경우 여러 요소가 포함된 배열(예: ['posts', postId]
)로 정의할 수도 있음.queryFn
Promise
를 반환queryFn
이 반환하는 데이터를 useQuery
의 data
속성으로 제공import { useQuery } from '@tanstack/react-query';
import React from 'react';
import axios from 'axios';
const ReactQueryPage = () => {
const fetchPost = () => {
return axios.get('http://localhost:3000/posts');
};
// useQuery는 컴포넌트가 시작할 때 실행됨(mount시)
const { data, isLoading, isError, error, refetch } = useQuery({
// 각각의 api 호출에 이름을 지어줌(unique 해야함)
queryKey: ['posts'],
//호출하고 싶은 api 함수
queryFn: fetchPost,
// api 로드 실패시 재시도 횟수 default 3번 더
retry: 1,
// 필요한 데이터만 추출
select: (data) => {
return data.data;
},
});
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>{error.message}</div>;
return (
<div>
{data?.map((item) => (
<div>{item.title}</div>
))}
<button onClick={refetch}>pass</button>
</div>
);
};
export default ReactQueryPage;
data 객체 형태
Devtools
Fresh 상태일때는 cache에서 데이터를 들고 옴
staleTime은 거의 변하지 않는 데이터에 적절하게 사용해야 함.
*커스텀 훅
: 비즈니스 로직과 UI를 구분하는 게 중요 -> useQuery부분 훅으로 분리
import React from 'react';
import { usePostQuery } from '../hooks/usePosts';
const ReactQueryPage = () => {
// 어느 컴포넌트에서든 쉽게 불러 올 수 있음
const { data, isLoading, isError, error, refetch } = usePostQuery();
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>{error.message}</div>;
return (
<div>
{ {data?.map((item) => (
<div>{item.title}</div>
))} }
</div>
);
};
export default ReactQueryPage;
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
const fetchPost = () => {
return axios.get(`http://localhost:3000/posts/${postId}`);
};
export const usePostQuery = () => {
return useQuery({
// 각각의 api 호출에 이름을 지어줌(unique 해야함)
queryKey: ['posts', postId],
//호출하고 싶은 api 함수
queryFn:fetchPost(),
// api 로드 실패시 재시도 횟수 default 3번 더
retry: 1,
// 필요한 데이터만 추출
select: (data) => {
return data.data;
},
});
};
import { useQueries } from '@tanstack/react-query';
import axios from 'axios';
import React from 'react';
const DetialPage = () => {
const ids = [1, 2, 3, 4];
const fetchPostDetail = (id) => {
return axios.get(`http://localhost:3000/posts/${id}`);
};
const results = useQueries({
queries: ids.map((id) => {
return {
queryKey: ['posts', 'id'],
queryFn: () => fetchPostDetail(id),
};
}),
// 필드 정해서 원하는 데이터 추출해서 넣을 수 있음
combine: (results) => {
return { data1: results.map((result) => result.data.data) };
},
});
return <div>DetialPage</div>;
};
export default DetialPage;