서버에서 받아온 데이터를 패칭 React-query 사용하여 인피니티 스크롤
npm i @tanstack/react-query@5
npm i @tanstack/react-query-devtools@5 -D
react-query와 devtool을 설치해주고 Provider를 만든다.
Provider는 children을 통해 감싸주어 그 안에서 react-query를 공유하고 사용할 수 있게 할 수 있다.
서버액션과 라우트 핸들러에서만 사용가능
cache:no-store가 기본값 force-cache하면 캐시함
같은 주소에 fetch가 여러번 있을떄 한번만 실행하게 해줌 (한명이 여러번)
처음 값을 Request Memoization에 저장한 후 다음에 재사용함.
여러 사람이 같은 주소로 요청햇을 때 백엔드에 한번만 요청을 보내게 캐싱하는 것
데이터를 가져오는 것 캐싱을 하기 좋음.
캐싱 : 한번 가져온 데이터를 DB에서 다시 가져오지 않고 캐시에서 가져옴.
로딩 성공 실패를 쉽게 구현이 가능함.
querykey 구조를 잘 갖춰 놓으면 갱신 및 관리가 편함.
function RQProvider({ children }: Props) {
const [client] = useState(
new QueryClient({
defaultOptions: {
// react-query 전역 설정(옵션)
queries: {
refetchOnWindowFocus: false, //다른 창 옮겨갔다가 오면
retryOnMount: true, // 컴포넌트가 마운트 될 시에
refetchOnReconnect: false,
retry: false, // 다시 시도
},
},
})
);
return (
<QueryClientProvider client={client}>
{children}
<ReactQueryDevtools
initialIsOpen={process.env.NEXT_PUBLIC_MODE === "local"}
/>
</QueryClientProvider>
);
}
export default RQProvider;
QueryClientProvider로 children을 감싸 RQProvider가 감싸고 있는 layout 및 page는 모두 reactquery를 사용할 수 있게 하는 코드이다.
GCTIme(캐시타임) : 5분이 기본값. Inactive 일떄 GC타임이 돌아감.
GCTIme은 staleTime보다 무조건 커야함. Inactive이면 GC타임이 흘러가는데
Stale타임이 더 크면 GC타임이 더 작다면 Stale타임이 아직 다 흐르지 않았는데 Inactive Query가 사라지면 Stale타임의 의미가 없어짐.
ReactQuery에는 enabled라는 속성이 존재하여 조건을 줄 수 있다.
이 코드는 현재 useSession을 사용해 유저가 로그인 상태가 아니면 data를 불러오지 않는 것
const { data: session } = useSession();
const { data } = useQuery<Hashtag[]>({
queryKey: ["trends"],
queryFn: getTrend,
staleTime: 60000,
enabled: !!session?.user,
});
const {
data: user,
error,
isLoading,
} = useQuery<User, object, User, [_1: string, _2: string]>({
queryKey: ["users", username],
queryFn: getUser,
staleTime: 60000,
});
if (!user) {
return null;
}
if (error) {
return (
<>
<div className="flex gap-10 items-center">
<ArrowLeftCircle className="w-8 h-8" />
<h3 className="text-xl font-semibold">{user.nickname}</h3>
</div>
<div className="flex justify-between items-center">
<Image src={""} alt={user.nickname} width={150} height={150} />
<div className="flex-1">
<p className="text-lg font-semibold">{user.id}</p>
<p>@{user.nickname}</p>
</div>
<div className="text-2xl">계정이 존재하지 않음.</div>
</div>
</>
);
}
UseQuery에는 error 속성과 isLoading 속성이 있어 error 혹은 loading 상태일때 다른 값을 보여줄 수 있다. 혹은 error loading 처리
"use client";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Post as IPost } from "@/model/Post";
import Post from "../../_components/Post";
import { getUserPosts } from "../_lib/getUserPosts";
export default function UserPosts({ username }: { username: string }) {
const { data } = useQuery<
IPost[],
object,
IPost[],
[_1: string, _2: string, _3: string]
>({
queryKey: ["posts", "users", username],
queryFn: getUserPosts,
staleTime: 60000,
});
const queryClient = useQueryClient();
const user = queryClient.getQueryData(["users", username]);
if (user) return data?.map((post) => <Post key={post.postId} post={post} />);
}
1) useQueryClient()로 전역 QueryClient 인스턴스를 가져옴.
2) 같은 queryKey(["users", username])로 이미 캐싱된 사용자 데이터를 getQueryData()로 동기 조회한 후 네트워크 요청 없이 즉시 반환됨.
3) 캐시에 없으면 undefined가 오므로 분기 처리.
4) 동일한 queryKey를 쓰는 모든 컴포넌트가 캐시를 공유함.