queryFn
의 parameter로 Abort Signal을 받을 수 있고, 이를 통해 Query 취소가 가능하다.TanStack Query - Query Cancellation
queryFn
은 parameter로 queryFunctionContext
이란 객체를 기본적으로 받는다.
export const getTodos = async (queryFnContext) => {
const { queryKey, pageParam, signal, meta } = queryFnContext;
// queryKey: 배열형태의 쿼리키
// pageParam: useInfiniteQuery 사용 시 getNextPageParam 실행 시 적용
// signal: AbortSignal 을 의미 (네트워크 요청을 중간에 중단시킬 수 있는 장치)
// meta: query에 대한 정보를 추가적으로 메모를 남길 수 있는 string 필드
const response = await axios.get("http://localhost:5000/todos", { signal });
return response.data;
};
useQuery({
queryKey: ["todos"],
queryFn: getTodos,
})
// example: <div onClick={(event) => {}}
import axios from 'axios'
const query = useQuery({
queryKey: ['todos'],
queryFn: ({ signal }) => // queryFunctionContext의 signal 프로퍼티
axios.get('/todos', {
// Pass the signal to `axios`
signal, // signal 설정
}),
})
TanStack Query - Optimistic Updates
import { useInfiniteQuery } from "@tanstack/react-query";
import { useInView } from "react-intersection-observer";
import { fetchMovieData } from "../api/movie";
export default function MovieInfiniteScroll() {
const {
data: movies,
hasNextPage,
fetchNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ["movies"],
queryFn: fetchMovieData,
getNextPageParam: (lastPage) => {
console.log("getNextPageParam 호출");
console.log("lastPage: ", lastPage);
if (lastPage.page < lastPage.total_pages) {
console.log("다음 페이지로 pageParam 저장");
return lastPage.page + 1;
}
},
select: (data) => {
return data.pages.map((pageData) => pageData.results).flat();
},
});
console.log("hasNextPage:", hasNextPage);
console.log("movies: ", movies);
const { ref } = useInView({
threshold: 1,
onChange: (inView) => {
if (!inView || !hasNextPage || isFetchingNextPage) return;
fetchNextPage();
},
});
return (
<div>
{console.log("무한스크롤 UI 렌더링")}
<h1>영화 무한스크롤 예제</h1>
<ul style={{ marginBottom: 300 }}>
{movies?.map((movie) => (
<li key={movie.id}>{movie.title}</li>
))}
</ul>
<div
style={{
textAlign: "center",
backgroundColor: "green",
color: "white",
width: "100%",
height: 50,
}}
ref={ref}
>
Trigger to Fetch Here
</div>
</div>
);
}
queryFn 실행
→ 캐시 데이터 등록 { pages, pageParam }
→ getNextPageParam
실행 (리턴된 NextPageParam
는 훅 내부 메모리에 저장. 캐시에 저장X)
→ (NextPageParam
이 undefined이 아니면)hasNextPage
true
로 상태변경
→ fetchNextPage
실행
→ queryFn
실행 (이 때 내부적으로 저장되어 있던 NextPageParam을 queryFn 의 매개변수로 넘겨줌)
pages 와 pageParams를 갖는 캐시 데이터
- useQuery 에서는 QueryFn 의 반환값이 캐시데이터로 등록됨
- useInfiniteQuery 에서는 QueryFn 의 반환값은 pages 배열의 요소로 추가되고, 매개변수로 받았던 pageParam은 pageParams 배열의 요소로 추가됨
useMemo
와 같은 memoization 적용을 특히 고려해야 합니다.