<aside> 💡
npm install @tanstack/react-query @tanstack/react-query-devtools
</aside>
최상위 컴포넌트에 provider로 감싸야 하는데,
client 방식이라 app/layout.ts에 직접 사용할 수 없다.
컴포넌트로 만든 후에 import 하여 사용해야함
2-1) 어떤 컴포넌트/QueryProvider.tsx
'use client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { useState } from 'react';
export default function QueryProvier({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
}
}
}));
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
set을 안 쓰나요?QueryClient 인스턴스는 한 번 생성되면 애플리케이션이 실행되는 내내 단 하나만 존재해야 합니다. 만약 도중에 setQueryClient로 새로운 인스턴스를 갈아 끼워버리면, 이전에 쌓여있던 모든 캐시 데이터가 날아가 버립니다.
그래서 여기서는 **"컴포넌트가 처음 마운트될 때 딱 한 번만 만들고, 그다음 리렌더링 때는 새로 만들지 않고 기존 걸 재사용하겠다"**는 목적으로 useState를 사용하는 것입니다.
useState를 쓰는 이유만약 layout.tsx나 Provider 안에서 그냥 변수로 선언하면 어떻게 될까요?
new QueryClient()가 매번 실행되어 새로운 인스턴스가 만들어집니다. 캐싱이 유지되지 않고 계속 초기화됩니다.useState(() => new QueryClient()) 형태를 사용하면, 리액트는 최초 렌더링 시에만 함수를 실행하고 그 결과값을 기억합니다. 이후 리렌더링 시에는 함수를 다시 실행하지 않고 저장된 값을 그대로 반환합니다.이 방식은 "불변하는 객체를 리액트의 생명주기 동안 안전하게 딱 하나만 유지하고 싶을 때" 사용하는 관용적인 패턴입니다.
2-2) 이후 app/layout에서 임포트하기
import QueryProvier from "@/lib/QueryProvider";
// ...
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased notoFont.className`}
>
<QueryProvier>
{children}
</QueryProvier>
</body>
</html>
);
}