React : TanStack Query LifeCycle, Options
TanStack Query LifeCycle
1. active 상태
최초에 데이터 fetching (가져오는 중..)
=> 가지고 온 직후에는 fresh한 상태 (신선한 상태)
=> 일정시간 동안 fresh 유지 (staleTime)
staleTime은 0이 기본값
staleTime을 10초로 설정하면 데이터의 fresh 상태가 10초동안 유지되고 stale 상태로 바뀜
=> stale 상태 (썩은 상태)
=> stale되면 데이터 refetch
refetch trigger
- refetchOnMount
- refetchOnWindowFocus
- refetchOnReconnect ..
2. inactive 상태 (현재 페이지에서 사용되지 않는 상태)
다시 사용된다면 active상태로 바뀜
3. deleted 상태 (cacheTime이 만료된 상태)
cacheTime : 캐시 데이터를 언제까지 보관할 것인지의 시간
(TanStack Query v5부터 gcTime)
상태 | 설명 |
fresh | 데이터를 새로 패칭할 필요가 없는 상태 staleTime이 지나지 않은 상태로, 캐시 데이터를 그대로 사용 가능 |
stale | 데이터를 새로 패칭해야 하는 상태 staleTime이 지난 후로, 새로운 데이터를 가져오기 위해 쿼리 실행 |
active | 현재 컴포넌트에서 사용 중인 쿼리 상태 컴포넌트가 마운트되어 쿼리를 사용하고 있을 때 |
inactive | 더 이상 사용되지 않는 쿼리 상태 컴포넌트가 언마운트되거나 쿼리가 더 이상 필요하지 않을 때 |
deleted | 캐시에서 제거된 쿼리 상태 gcTime 이 지나면 쿼리가 캐시에서 삭제되었을 때 |
fetching | 데이터를 서버에서 가져오고 있는 상태 이 상태에서는 isFetching이 true로 설정 |
default config(기본 설정)
기본 설정 의미
staleTime: 0 | useQuery 또는 useInfiniteQuery에 등록된 queryFn 을 통해 fetch 받아온 데이터는 항상 stale data 취급 |
refetchOnMount: true | useQuery 또는 useInfiniteQuery 가 있는 컴포넌트가 마운트 시 stale data 를 refetch 자동 실행 |
refetchOnWindowFocus: true | 실행중인 브라우저 화면을 focus 할 때 마다 stale data를 refetch 자동 실행 |
refetchOnReconnect: true | Network 가 끊겼다가 재연결 되었을 때 stale data를 refetch 자동 실행 |
gcTime(cacheTime): 5분 (1000 * 60 * 5 ms) |
useQuery 또는 useInfiniteQuery가 있는 컴포넌트가 언마운트 되었을 때 inactive query라 부르며, inactive 상태가 5분 경과 후 GC(가비지콜렉터)에 의해 cache data 삭제 처리 |
retry: 3 | useQuery 또는 useInfiniteQuery에 등록된 queryFn 이 API 서버에 요청을 보내서 실패하더라도 바로 에러를 띄우지 않고 총 3번까지 재요청을 자동으로 시도 |
테스트
테스트를 위해 ReactQueryDevtools 설치
터미널
yarn add @tanstack/react-query-devtools
main.jsx에 devtools 삽입
//main.jsx
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={false} />
<App />
</QueryClientProvider>
</React.StrictMode>
)
staleTime을 5초로 설정
queryClient
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5000
}
}
});
5초동안 서버에 새로운 데이터를 요청하지 않음
( ∵ 서버에 revalidate 요청은 stale 상태일 때 요청)
queryClinet말고 useQuery시에도 설정가능
const { data: todos, isPending, isError } = useQuery({
queryKey: ["todos"],
queryFn: fetchTodos,
staleTime: 5000,
});
동일하게 작동
refetchOnMount
마운트 될 때마다 refetch
queryClient
const queryClient = new QueryClient(
{
defaultOptions: {
queries: {
staleTime: 1000, // refetch 확인용
refetchOnMount: true //기본값
}
}
}
);
todos가 사용되지 않는 empty 페이지에서는 inactive상태가 되었다가 다시 사용되는 페이지로 이동하면 fresh로 바뀜
=> todos를 사용하는 컴포넌트가 마운트 되면 refetch가 일어남
refetchOnMount : false로 변경하면
사용하는 컴포넌트가 마운트되어도 계속 stale => refetch가 일어나지 않음
refetchOnWindowFocus
포커스를 받으면 refetch
const queryClient = new QueryClient(
{
defaultOptions: {
queries: {
staleTime: 1000, // refetch 확인용
refetchOnWindowFocus: true, // 기본값
}
}
}
);
포커스를 다른 탭에 맞췄다가 다시 돌아오면 fresh 상태 => 포커스를 받으면 refetch
refetchOnWindowFouch : false로 변경하면
포커스를 잃었다가 다시 받아도 fresh상태가 아닌 계속 stale 상태로 유지 => refetch가 일어나지 않음
refetchOnReconnect도 동일, 기본값 true
true => 인터넷이 끊겼다가 다시 연결되면 refetch
flase => 인터넷이 끊겼다가 다시 연결되어도 refetch X
gcTime (inactive 상태의 데이터를 언제까지 저장하고 있을 지)
queryClient
const queryClient = new QueryClient(
{
defaultOptions: {
queries: {
staleTime: 1000,
gcTime: 2000,
}
}
}
);
inacive상태의 todos가 2초있다가 삭제되는 것을 확인
retry (요청 실패시 재요청 시도 횟수, 기본값 3)
일부러 로컬호스트 주소를 DB와 다른 곳으로 설정하고 요청
3번 재시도를 한 끝에 오류메시지를 보여줌
다른 설정들과 동일하게 설정 가능
헷갈리는 개념 정리
staleTime과 gcTime
- staleTime : 얼마의 시간이 흐른 뒤에 stale 취급할 건지 (default: 0)
- gcTime : inactive 된 이후로 메모리에 얼마만큼 있을건지 (default: 5분, gcTime 0되면 삭제처리)
staleTime 과 stale / fresh 의 관계
- staleTime > 0 이면, fresh data
- staleTime = 0 이면, stale data
isPending과 isFetching
- isPending : 새로운 캐시 데이터를 서버에서 받고 있는 지 여부
- isFetching: 서버에서 데이터를 받고 있는 지 여부
- => 캐시 데이터가 있는 경우 서버로부터 데이터는 받고있으니 isFetching : true
하지만 새로운 캐시 데이터를 받고있는 것은 아니니 isPending : false
useQuery할 때, 반드시 알아야 하는 옵션
enabled : 쿼리(queryFn) 실행 여부를 제어, 기본값 true
값을 false로 설정하면 쿼리가 자동으로 실행되지 않음
이 옵션을 사용하여 특정 이벤트 발생 시 쿼리를 실행
예시 코드
예시1)
const { data, refetch } = useQuery({
queryKey: ["todos"],
queryFn: getTodos,
enabled: false
});
return (
<div>
<button onClick={() => refetch()}>데이터 불러오기</button>
</div>
);
// 데이터 불러오기 버튼을 클릭하면 getTodos로 데이터를 가져옴
예시2)
// Dependent Query 예제 (순차적 query 실행)
// Get the user
const { data: user } = useQuery({
queryKey: ['user', email],
queryFn: getUserByEmail,
})
const userId = user?.id
// Then get the user's projects
const {
status,
fetchStatus,
data: projects,
} = useQuery({
queryKey: ['projects', userId],
queryFn: getProjectsByUser,
// 쿼리는 userId가 존재하는 경우에만 실행
enabled: !!userId
})
select : 쿼리 함수에서 반환된 데이터를 변형
데이터의 특정 부분만 선택하거나 데이터를 변환해서 사용할 때 유용
( 단, 캐시 데이터는 원본 데이터 유지 )
예시 코드
예시)
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery({
queryKey: ["user"],
queryFn: fetchUser,
select: user => user.username
});
return <div>Username: {data}</div>
}
// 가져온 user데이터들의 username값들만 뽑아옴