[React] TanStack Query(3)

Hyowmls·2024년 6월 12일
0
post-thumbnail

Query Cancellation

불필요한 네트워크 요청을 제거하는것

  • queryFn은 매개변수로 QueryFucntionContext라는 객체를 받는다

예시코드

export const getTodos = async (queryFnContext) => {
  const { queryKey, pageParam, signal, meta } = queryFnContext;
  const response = await axios.get("http://localhost:5000/todos", { signal });
  return response.data;
};

useQuery({
  queryKey: ["todos"],
  queryFn: getTodos,
})
  • queryKey: 배열형태의 쿼리키
  • pageParam: useInfiniteQuery 사용 시 getNextPageParam 실행 시 적용
  • signal: AbortSignal 을 의미 (네트워크 요청을 중간에 중단시킬 수 있는 장치)
  • meta: query에 대한 정보를 추가적으로 메모를 남길 수 있는 string 필드

페이지 컴포넌트 unmount시 쿼리 취소

import axios from 'axios'

const query = useQuery({
  queryKey: ['todos'],
  queryFn: ({ signal }) =>
    axios.get('/todos', {
      // Pass the signal to `axios`
      signal,
    }),
})

수동으로 쿼리 취소

const query = useQuery({
  queryKey: ['todos'],
  queryFn: async ({ signal }) => {
    const resp = await fetch('/todos', { signal })
    return resp.json()
  },
})

const queryClient = useQueryClient()

return (
  <button
    onClick={(e) => {
      e.preventDefault()
      queryClient.cancelQueries({ queryKey: ['todos'] })
    }}
  >
    Cancel
  </button>
)

Optimistic Updates

서버 요청이 정상적으로 잘 될거란 가정하에 UI를 먼저 변경하고 서버에 요청하는 방식
혹시라도 서버 요청이 실패하는 경우, UI를 원상복구 시킨다

예시코드

onMutate: async (newTodo) => {
  console.log("onMutate 호출");
  await queryClient.cancelQueries({ queryKey: ["todos"] });

  const previousTodos = queryClient.getQueryData(["todos"]);

  queryClient.setQueryData(["todos"], (old) => [...old, newTodo]);

  return { previousTodos };
},
onError: (err, newTodo, context) => {
  console.log("onError");
  console.log("context:", context);
  queryClient.setQueryData(["todos"], context.previousTodos);
},
onSettled: () => {
  console.log("onSettled");
  queryClient.invalidateQueries({ queryKey: ["todos"] });
},

Prefetching

페이지 이동 전에 이동할 페이지의 쿼리를 백그라운드에서 미리 호출한다
캐시 데이터가 있는 상태로 해당 페이지로 이동 시 로딩없이 바로 UI를 볼 수 있다

예시코드

const prefetchTodos = async () => {
  await queryClient.prefetchQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
  })
}
  • The results of this query will be cached like a normal query
  • prefetch 할 queryKey와 queryFn 은 이동할 페이지의 쿼리와 동일해야 적절합니다.

Paginated/Lagged queries

다른 페이지 클릭 시 매번 Loading UI를 보여주기 보다는 기존 UI를 유지하다가 서버로부터 새로운 데이터를 받아왔을때 바꾸는 방식

  • useQuery의 옵션 중 keepPreviousData를 true로 바꾸면 이전 캐시데이터를 기반으로 isLoading 여부를 판단하게 된다

Infinite queries

Data Fetching이 일어날 때 마다 기존 리스트 데이터에 Fetched Data를 추가하고자 할 때 유용하게 사용하는 hook

  • 더보기, 무한 스크롤에 사용하기 적합하다

예시코드

const fetchProjects = async ({ pageParam = 0 }) => {
    const res = await fetch('/api/projects?cursor=' + pageParam)
    return res.json()
  }

  const {
    data,
    error,
    fetchNextPage,
    hasNextPage,
    isFetching,
    isFetchingNextPage,
    status,
  } = useInfiniteQuery({
    queryKey: ['projects'],
    queryFn: fetchProjects,
    getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
  })

page와 pageParams를 갖는 캐시 데이터

  • useQuery에서는 QueryFn의 반환값이 캐시데이터로 등록된다
  • useInfiniteQuery에서는 QueryFn의 반환값은 pages배열의 요소로 추가되고, 매개변수로 받았던 pageParam은 pageParams 배열의 요소로 추가된다

useInfiniteQuery 사용 시 주의사항

  • 훅 내부적인 동작원리로 인하여 예상보다 잦은 리렌더링이 발생할 수 있다
  • 연산량이 많은 코드가 있는 경우 useMemo와 같은 memoization 기법을 고려해야한다
  • 리렌더링이 발생한다고 해서 실제 브라우저 렌더링이 발생하는것은 아니다

0개의 댓글