마지막으로 프로젝트에 적용해본 리액트쿼리의 버전이 v3버전이고 그 이후로는 사용해본 적이 없어서 리액트쿼리 공식문서를 다시 정독해보고 v5버전에서 새롭게 추가되거나 바뀐 내용들을 정리해본다. 미래의 내가 다시 문서를 읽으면서 해석하는 시간을 줄이기 위함이기에 나에게 필요한 내용 위주로 정리해 보겠음.
tanstack query(react query) v5는 React18버전~ 그 이후부터 사용이 가능하다.
v5부터는 단일 객체 형식만 지원하는 방식으로 변경되었다.
- useQuery(key, fn, options)
+ useQuery({ queryKey, queryFn, ...options })
- useInfiniteQuery(key, fn, options)
+ useInfiniteQuery({ queryKey, queryFn, ...options })
- useMutation(fn, options)
+ useMutation({ mutationFn, ...options })
- useIsFetching(key, filters)
+ useIsFetching({ queryKey, ...filters })
- useIsMutating(key, filters)
+ useIsMutating({ mutationKey, ...filters })
이전에 remove메서드는 쿼리캐시에서 쿼리를 제거하는 용도로 사용되었고, 유저가 로그아웃한 경우 데이터가 더이상 필요하지 않을 때 명령적으로 데이터를 제거하는 좋은 방법으로 사용이 되었다.
하지만 쿼리가 계속 활성화되고 있을 때는 다음 리렌더링의 하드 로딩을 트리거할 뿐이기 때문에 그다지 의미가 크지 않다.
여전히 쿼리를 제거해야하는 경우 queryClient.removeQueries({queryKey: key})
를 사용할 수 있다.
cacheTime은 얼핏보면 데이터가 캐시되는 시간으로 인식되기에 gcTime
으로 이름이 변경되었다.
gcTime을 설정하면 쿼리가 사용되고 있는 시간이 아닌 쿼리가 사용되지 않기 시작할 때 부터의 시간을 의미하고 이 시간이 지나고나면 쿼리는 가비지 수집된다. gc는 ‘garbage collect’ time의 약자이다.
삭제,
maxPages` 추가v4에서는 refetchPage
함수를 통해 무한 쿼리에서 페이지를 다시 가져오는것을 정의할 수 있었다. 하지만 모든 페이지를 다시 가져오는 것은 UI에서 불일치를 초래할 수 있다. 또한 이 옵션은 queryClient.refetchQueries
에서도 가능하지만 일반 쿼리가 아닌 무한 쿼리에서만 사용할 수 있었다.
또한 많은 페이지를 가져올수록 소비하는 메모리가 많아지고 모든 페이지가 순차적으로 리패치됨에 따라 리패치의 속도가 느려질 수 있다.
v5에서는 무한 쿼리에 maxPages
옵션이 추가되어 쿼리 데이터에 저장되는 페이지 수를 제한하고 다시 가져오기를 할 수 있다. 이 새로운 기능은 처음에 refetchPage에서 정의되었던 케이스들을 관련이슈 없이 처리할 수 있다.
무한쿼리는 일반 쿼리처럼 미리 가져오기가 가능하다(prefetching). 기본은 첫번째페이지만 미리 가져오고 쿼리키에 저장된다. 만약 더 많은 페이지를 가져오고 싶다면 pages
옵션을 설정하면 된다.
이전에 queryFn에 pageParam으로 undefined를 전달하고, queryFn의 pageParam파라미터로 기본값을 할당했다. 이것은 쿼리캐시에 undefined가 저장되어 장애를 유발했다.
대신에 이제는 명백한 initialPageParam을 무한 쿼리의 옵션으로 전달할 수 있게 되었다. 이것은 첫번째 페이지에 pageParam으로 사용된다.
useInfiniteQuery({
queryKey,
- queryFn: ({ pageParam = 0 }) => fetchSomething(pageParam),
+ queryFn: ({ pageParam }) => fetchSomething(pageParam),
+ initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.next,
})
이전에 getNextPageParam
이나 getPreviousPageParam
에서 반환된 pageParam
을 fetchNextPage
나 fetchPreviousPage
의 pageParam
값으로 전달해서 덮어쓸 수 있었다. 이 기능은 리패치에서 작동되지 않았고 폭넓게 알려지거나 사용되지 않았다. 이것은 getNextPageParam 이 무한 쿼리에서 요구됨을 의미한다.
getNextPageParam
과 getPreviousPageParam
에서 반환된 null은 더 이상 페이지가 존재하지 않음을 의미한다.
v4에서 더 이상 페이지가 없다는 것을 명시적으로 가리키기 위해 undefined를 전달한것을 이제는 null로 확인한다.
서버에서 retry
의 기본값이 3에서 0으로 변경되었다.
사전 로딩(prefetching)의 경우 재시도 기본값은 항상 0이었다. 하지만 React 18에서 suspense를 활성화한 쿼리들도 이제 서버에서 직접 실행될 수 있기 때문에 서버에서는 재시도하지 않도록 확실히 해야 한다.
Hydrate
컴포넌트의 이름이 HydrationBoundary
로 변경되었다.
HydrationBoundary
는 더 이상 mutation을 hydrate하지 않고 쿼리에만 적용된다. mutation에 적용하려면 낮은 레벨의 hydate
API나 persistQueryClient
플러그인을 사용해야 한다.
쿼리가 hydrate되는 시점에 약간 변화가 존재한다. 이전처럼 SSR에서 새로운 쿼리들은 렌더시 hydrate된다. 하지만 캐시에 이미 존재했던 쿼리들은 영향이 있다. 앱이 시작하고 처음 한번의 hydrate는 상관이 없지만 서버 컴포넌트를 사용하고 있고 새로운 데이터를 내려줄 때 페이지가 리렌더 되기 전에 깜빡임이 있을 수 있다.
onSuccess
, onError
, onSettled
와 같은 콜백이 없어졌다.
status: loading
⇒ status: pending
isLoading
⇒ isPending
isInitialLoading
⇒ isLoading
hashQueryKey
⇒ hashKey
getQueryData
와 getQueryState
의 인자는 오직 queryKey만 받는다.
refetchInterval
의 콜백함수에서 data가 없어지고 query만 전달된다. 여전히 query.state.data
로 데이터를 전달받을 수 있지만 select옵션으로 데이터를 변형해 올 수 없다.
isDataEqual
옵션이 없어지고 동일한 기능을 하는 structuralSharing
이 추가되었다.
isDataEqual: (oldData, newData) => customCheck(oldData, newData)
+ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData)
visibilitychange
이벤트만 사용됨.
useErrorBoundary옵션을 더 프레임워크에 종속되지 않으면서 리액트에서 ‘use’ 로 시작하면 훅을 나타고, ErrorBoundary는 컴포넌트 이름을 나타내는 의미로 고착되어 있어 이름에서의 혼동을 피하기 위해 기능을 더 명확하게 반영할 수 있는 throwOnError
로 변경되었다.
navigator.onLine이 Chromium기반 브라우저에서 잘 동작하지 않는 이슈가 있었다.
항상 상태는 online: true에서 시작하고 online, offline이벤트로 업데이트.
인터넷 연결이 없는 오프라인 앱에서는 거짓양성이 나타날 수 있으므로 주의해야 한다.
suspense: boolean
이 삭제되고 useSuspenseQuery
, useSuspenseInfiniteQuery
, useSuspenseQueries
로 변경되었다.
const { data: post } = useSuspenseQuery({
// ^? const post: Post
queryKey: ['post', postId],
queryFn: () => fetchPost(postId),
})