무엇
QueryClientProvider는 QueryClient 하나를 컴포넌트 트리 전체에 공급하는 컴포넌트다. 이 아래에서만 useQuery, useMutation 같은 훅이 동작한다.
React Query를 쓰는 앱은 최상단을 이 provider로 감싸는 데서 시작한다. client prop에 QueryClient 인스턴스를 넘긴다.
무슨 일이 일어나나
QueryClientProvider는 React Context로 client를 흘려보낸다. 그 아래 어느 깊이의 컴포넌트든, prop으로 넘기지 않아도 훅이 이 client를 찾아 쓴다. client 안에는 QueryCache와 MutationCache, 기본 옵션이 들어 있어 앱의 모든 캐시가 이 한 인스턴스에 모인다.
그래서 QueryClient는 컴포넌트 밖에서 한 번만 만들어야 한다. 컴포넌트 본문에서 만들면 리렌더마다 새 인스턴스가 생겨 캐시가 통째로 날아간다.
사용법
client prop이 필수이고 타입은 QueryClient다. 인스턴스를 모듈 최상단이나 앱 루트에서 만들어 넘긴다.
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
// 컴포넌트 밖에서 한 번만 만든다
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<Routes />
</QueryClientProvider>
)
}
앱 전체의 기본 캐싱 성격은 QueryClient를 만들 때 정한다.
const queryClient = new QueryClient({
defaultOptions: {
queries: { staleTime: 60 * 1000 },
},
})
실무 예시
서버 렌더링 환경에서는 요청마다 client가 섞이면 안 되므로, 클라이언트 컴포넌트 안에서 useState로 한 번만 만들어 고정한다. 브라우저에서는 리렌더에도 같은 인스턴스가 유지된다.
'use client'
import { useState } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
export function Providers({ children }: { children: React.ReactNode }) {
// 리렌더에도 같은 client를 유지한다
const [queryClient] = useState(() => new QueryClient())
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
)
}
왜 중요한가
QueryClientProvider 없이는 React Query의 어떤 훅도 동작하지 않는다. 앱에 딱 한 번, 최상단에 두는 설정 컴포넌트라 눈에 자주 띄지는 않지만 없으면 아무것도 안 된다. 여기서 넘기는 QueryClient의 기본 옵션(staleTime, gcTime, retry 등)이 앱 전체의 캐싱 성격을 정하므로 사실상 React Query 설정의 출발점이다.
Reference