useQuery
,useInfiniteQuery
,useMutation
์ด ์ด์ ๋ ๊ฐ์ฒด ํ์๋ง ์ง์ํ๋๋ก ๋ณ๊ฒฝ๋์์ต๋๋ค.- v4์์๋
useQuery(key, fn, options)
,useQuery({ queryKey, queryFn, ...options })
๋ ํํ๋ฅผ ๋ชจ๋ ์ง์ํ๋๋ฐ ์ด๋ ์ ์ง๋ณด์๊ฐ ํ๋ค๊ณ , ๋งค๊ฐ ๋ณ์ ํ์ ์ ํ์ธํ๊ธฐ ์ํ ๋ฐํ์ ๊ฒ์ฌ๋ ํ์ํ๊ธฐ ๋๋ฌธ์ ์ค๋ก์ง๊ฐ์ฒด
ํ์๋ง ์ง์ํ๋๋ก 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 })
- queryClient.isFetching(key, filters)
+ queryClient.isFetching({ queryKey, ...filters })
- queryClient.ensureQueryData(key, filters)
+ queryClient.ensureQueryData({ queryKey, ...filters })
- queryClient.getQueriesData(key, filters)
+ queryClient.getQueriesData({ queryKey, ...filters })
- queryClient.setQueriesData(key, updater, filters, options)
+ queryClient.setQueriesData({ queryKey, ...filters }, updater, options)
- queryClient.removeQueries(key, filters)
+ queryClient.removeQueries({ queryKey, ...filters })
- queryClient.resetQueries(key, filters, options)
+ queryClient.resetQueries({ queryKey, ...filters }, options)
- queryClient.cancelQueries(key, filters, options)
+ queryClient.cancelQueries({ queryKey, ...filters }, options)
- queryClient.invalidateQueries(key, filters, options)
+ queryClient.invalidateQueries({ queryKey, ...filters }, options)
- queryClient.refetchQueries(key, filters, options)
+ queryClient.refetchQueries({ queryKey, ...filters }, options)
- queryClient.fetchQuery(key, fn, options)
+ queryClient.fetchQuery({ queryKey, queryFn, ...options })
- queryClient.prefetchQuery(key, fn, options)
+ queryClient.prefetchQuery({ queryKey, queryFn, ...options })
- queryClient.fetchInfiniteQuery(key, fn, options)
+ queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options })
- queryClient.prefetchInfiniteQuery(key, fn, options)
+ queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options })
- queryCache.find(key, filters)
+ queryCache.find({ queryKey, ...filters })
- queryCache.findAll(key, filters)
+ queryCache.findAll({ queryKey, ...filters })
2. โญ๏ธ 'queryClient.getQueryData', 'queryClient.getQueryState' now accepts queryKey only as an Argument
queryClient.getQueryData
์ ์ธ์๊ฐqueryKey
๋ง ๋ฐ๋๋ก v5์์ ์์ ๋์์ต๋๋ค.
- queryClient.getQueryData(queryKey, filters)
+ queryClient.getQueryData(queryKey)
- ๋ง์ฐฌ๊ฐ์ง๋ก
queryClient.getQueryState
๋ ์ธ์๊ฐqueryKey
๋ง ๋ฐ๋๋ก v5์์ ์์ ๋์์ต๋๋ค.
- queryClient.getQueryState(queryKey, filters)
+ queryClient.getQueryState(queryKey)
- useQuery์ ์ต์
์ธ
onSuccess
,onError
,onSettled
๊ฐ ์ ๊ฑฐ๋์์ต๋๋ค.- ํด๋น ์ฝ๋ฐฑ ํจ์๋ค์ ๊ฐ๋จํ๊ณ , ์ง๊ด์ ์ด๋ผ ๊ต์ฅํ ์ ์ฉํ์ง๋ง ๋ฒ๊ทธ๋ฅผ ์ ๋ฐ ํ ์ ์์ต๋๋ค.
- ์์ธํ ๋ด์ฉ์ Tanstack Query maintainer์ธ tkdodo ๋ธ๋ก๊ทธ ํฌ์คํ ์ ์ฐธ๊ณ ํด์ฃผ์๊ธฐ ๋ฐ๋๋๋ค.
-
useQuery์
remove
๋ฉ์๋๊ฐ ์ ๊ฑฐ๋์์ต๋๋ค. ์ด์ ์๋remove
๋ฉ์๋๋ observer์๊ฒ ์๋ฆฌ์ง ์๊ณ ์ฟผ๋ฆฌ๋ฅผ queryCache์์ ์ ๊ฑฐํ๋๋ฐ ์ฌ์ฉํ์ต๋๋ค.- ์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๋ฅผ ๋ก๊ทธ์์ ํ ๋์ ๊ฐ์ด ๋ ์ด์ ํ์ํ์ง ์์ ๋ฐ์ดํฐ๋ฅผ ์ ๊ฑฐํ ๋์ ๊ฐ์ ๊ฒฝ์ฐ์ ํ์ฉ ํ ์ ์๋ ๋ฉ์๋์์ต๋๋ค.
-
ํ์ง๋ง, query๊ฐ ์์ง ํ์ฑํ๋ ์ํ์์ ์ด
remove
๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒ์ ๋ค์ ๋ฒ ๋ฆฌ๋ ๋๋ง ํ ๋hard loading ์ํ
๋ฅผ ํธ๋ฆฌ๊ฑฐํ๊ธฐ ๋๋ฌธ์ ํฉ๋ฆฌ์ ์ด์ง ๋ชปํฉ๋๋ค.- ์ฌ๊ธฐ์,
hard loading ์ํ
๋? ๋ฐ์ดํฐ๊ฐ ์๋ ์ฆ, ์ด๊ธฐ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ฌ ๋ ๋ก๋ฉ ์ํ๋ฅผ ๋งํฉ๋๋ค. - useQuery์์ ์์ฃผ ์ฌ์ฉํ๋
isLoading
์ด ์ด๋ฐhard loading ์ํ
์ธ ๊ฒฝ์ฐ์๋ง์ฐธ(true)
์ ๋๋ค. - When we refetch a query, it doesn't set isLoading true.
- ์ฌ๊ธฐ์,
-
ํ์ง๋ง!! ๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ ์ฟผ๋ฆฌ๋ฅผ ์ ๊ฑฐํด์ผ ๋๋ค๋ฉด
queryClient.removeQueries
๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
const queryClient = useQueryClient();
const query = useQuery({ queryKey, queryFn });
- query.remove()
+ queryClient.removeQueries({ queryKey })
isDataEqual
ํจ์๋ query์์ resolved๋ ๋ฐ์ดํฐ๋ก์ ์ด์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ์ง ์๋๋ฉด ์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ์ง ํ์ธํ๋๋ฐ ์ฌ์ฉํ์ต๋๋ค.- ์ด์ ๋
isDataEqual
์ ์ฌ์ฉํ์ง ์๊ณ , ๋์ผํ ๊ธฐ๋ฅ์ผ๋ก์structuralSharing
์ผ๋ก ํ์ฉํ ์ ์์ต๋๋ค.
import { replaceEqualDeep } from '@tanstack/react-query'
- isDataEqual: (oldData, newData) => customCheck(oldData, newData)
+ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData)
cacheTime
์ดgcTime
์ผ๋ก ๋ณ๊ฒฝ๋์์ต๋๋ค.- ๋ค์ด๋ฐ์ด ๋ณ๊ฒฝ๋ ์ด์ ๋ ๋ง์ ์ฌ๋๋ค์ด
cacheTime
์ ๋ง์น "๋ฐ์ดํฐ๊ฐ ์บ์๋๋ ์๊ฐ"์ผ๋ก ์ฐฉ๊ฐํ๊ธฐ ๋๋ฌธ์ ๋๋ค. - ํ์ง๋ง, ์ค์ ๋ก query๊ฐ ๊ณ์ ์ฌ์ฉ๋๋ ํ
cacheTime
์ ์๋ฌด ์ผ๋ ํ์ง ์๊ณ , query๊ฐ ๋์ด์ ์ฌ์ฉ๋์ง ์๋ ์์ ์ ์์๋ฉ๋๋ค. ๊ทธ๋ฆฌ๊ณcacheTime
์๊ฐ์ด ์ง๋๋ฉด ์บ์๊ฐ ๋์ด์ ์ปค์ง๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด ๋ฐ์ดํฐ๋garbage collected
๋ฉ๋๋ค.
- ๋ค์ด๋ฐ์ด ๋ณ๊ฒฝ๋ ์ด์ ๋ ๋ง์ ์ฌ๋๋ค์ด
- ๋ฐ๋ผ์, ์๋ฏธ์์ ํผ๋์ ์ค์ด๊ธฐ ์ํด
cacheTime
์์gcTime
์ผ๋ก ๋ณ๊ฒฝ๋์์ต๋๋ค.
const MINUTE = 1000 * 60;
const queryClient = new QueryClient({
defaultOptions: {
queries: {
- cacheTime: 10 * MINUTE,
+ gcTime: 10 * MINUTE,
},
},
})
- ๊ธฐ์กด์
ErrorBoundary
์ ์๋ฌ๋ฅผ ๋์ง๊ธฐ ์ํด ์ฌ์ฉํ๋ ์ต์ ์ธuseErrorBoundary
๋ฅผ ํน์ ํ๋ ์์ํฌ์ ์ข ์๋์ง ์์ผ๋ฉด์, ๋ฆฌ์กํธ ์ปค์คํ ํ ์ ์ ๋ฏธ์ฌ์ธuse
์ErrorBoundary
์ปดํฌ๋ํธ๋ช ๊ณผ ํผ๋์ ํผํ๊ธฐ ์ํด,throwOnError
๋ก ๋ณ๊ฒฝ๋์ต๋๋ค.
const todos = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
- useErrorBoundary: true,
+ throwOnError: true,
})
- v5 ๋ถํฐ๋ error์ ๊ธฐ๋ณธ ํ์
์ด
Error
์ ๋๋ค. ๋ณ๊ฒฝ๋ ์ด์ ๋ ๋ง์ ์ฌ์ฉ์๋ค์ด ๊ธฐ๋ํ๋ ๊ฒฐ๊ณผ์ด๊ธฐ ๋๋ฌธ์ ๋๋ค.
// const error: Error
const { error } = useQuery({
queryKey: ["groups"],
queryFn: fetchGroups,
});
- ๋ง์ฝ
์ปค์คํ ์๋ฌ
๋ฅผ ํ์ฉํ๊ฑฐ๋Error
๊ฐ ์๋ ๊ฒ์ ํ์ฉํ๊ณ ์ถ๋ค๋ฉด, ์๋ ์์ ์ฒ๋ผ ํ์ ์ ๊ตฌ์ฒดํํ ์ ์์ต๋๋ค.
// const error: string | null
const { error } = useQuery<Group[], string>({
queryKey: ["groups"],
queryFn: fetchGroups,
});
- โ
keepPreviousData
์ต์ ๊ณผisPreviousData
ํ๋๊ทธ๊ฐ ์ ๊ฑฐ๋์์ต๋๋ค.- ์๋ํ๋ฉด ์ด๋ค์ ๊ฐ๊ฐ
placeholderData
์ `isPlaceholderData ํ๋๊ทธ์ ๊ฑฐ์ ์ ์ฌํ๊ฒ ๋์ํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
- ์๋ํ๋ฉด ์ด๋ค์ ๊ฐ๊ฐ
- ์๋ ์์ ๋
placeholderData
๋ฅผ ํ์ฉํ๋ฉด์ ์ด์ ์keepPreviousData
์ต์ ์true
๋ก ์คฌ์๋์ ๋์ผํ ๊ธฐ๋ฅ์ ์ํํ๊ธฐ ์ํดidentity function
์ ํ์ฉํ๋placeholderData
์ Tanstack Query์ ํฌํจ๋keepPreviousData
ํจ์๋ฅผ ์ถ๊ฐํ์์ต๋๋ค.
import {
useQuery,
+ keepPreviousData
} from "@tanstack/react-query";
const {
data,
- isPreviousData,
+ isPlaceholderData,
} = useQuery({
queryKey,
queryFn,
- keepPreviousData: true,
+ placeholderData: keepPreviousData
});
- ๋๋, ์ง์
identity function
์ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ๋ ์์ต๋๋ค.
useQuery({
queryKey,
queryFn,
placeholderData: (previousData, previousQuery) => previousData,
// identity function with the same behaviour as `keepPreviousData`
});
-
์ฌ๊ธฐ์ ์ ๋ณ๊ฒฝ์ฌํญ์๋ ๋ช ๊ฐ์ง ์ฃผ์์ฌํญ์ด ์์ต๋๋ค.
-
placeholderData
๋ ํญ์success
์ํ๋ฅผ ์ ์งํ๋ฉฐ, keepPreviousData๋ ์ด์ ์ฟผ๋ฆฌ ์ํ๋ฅผ ์๋ ค์ค๋๋ค. ๋ฐ์ดํฐ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ๊ฐ์ ธ์จ ํbackground refetch error
๊ฐ ๋ฐ์ํ๋ฉดplaceholderData
์success
์ํ๋ ์ค๋ฅ๋ผ๊ณ ๋๋ ์ ์์ต๋๋ค. ํ์ง๋ง ์๋ฌ ์์ฒด๊ฐ ๊ณต์ ๋์ง ์๊ธฐ ๋๋ฌธ์placeholderData์
๋์์ ๊ทธ๋๋ก ์ ์งํ๊ธฐ๋ก ๊ฒฐ์ ๋์ต๋๋ค. -
keepPreviousData
๋ฅผ ์ฌ์ฉํ ๋๋ ์ด์ ๋ฐ์ดํฐ์dateUpdatedAt
ํ์ ์คํฌํ๊ฐ ์ ๊ณต๋์๋๋ฐ,placeholderData
๋ฅผ ์ฌ์ฉํ๋ฉดdateUpdatedAt
์0
์ผ๋ก ์ ์ง๋ฉ๋๋ค. -
๋ง์ฝ, ํ์์คํฌํ๋ฅผ ํ๋ฉด์ ๊ณ์ ๋ณด์ฌ์ฃผ๊ณ ์ถ๋ค๋ฉด ์ด๋ฐ ๋์์ด ๋ถ๋ง์กฑ์ค๋ฌ์ธ ์ ์์ต๋๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ ์๋์ ๊ฐ์ด
useEffect
๋ฅผ ํ์ฉํ๋ฉด ํด๊ฒฐํ ์ ์์ต๋๋ค.
-
const [updatedAt, setUpdatedAt] = useState(0);
const { data, dataUpdatedAt } = useQuery({
queryKey: ["projects", page],
queryFn: () => fetchProjects(page),
});
useEffect(() => {
if (dataUpdatedAt > updatedAt) {
setUpdatedAt(dataUpdatedAt);
}
}, [dataUpdatedAt]);
- Tanstack Query๋
visibilitychange
์ด๋ฒคํธ๋ฅผ ์ง์ํ๋ ๋ธ๋ผ์ฐ์ ๋ง ์ง์ํ๋๋ก ๊ฒฐ์ ๋์ต๋๋ค. ๋ฐ๋ผ์, ์ด์ visibilitychange
์ด๋ฒคํธ๋ง ๋ ์ ์ ์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค. - ์ด๋ฅผ ํตํด ๋ค์ focus ๊ด๋ จ ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋์์ต๋๋ค.
- ์ปค์คํ
queryClient ์ธ์คํด์ค๋ฅผ ์ํด ์ปค์คํ
context
prop์ด ์ ๊ฑฐ๋์์ต๋๋ค. - ๊ธฐ์กด v4์์๋
context
๋ฅผ ๋ชจ๋ react query hooks์ ์ ๋ฌํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ์ต๋๋ค. ์ด๋MicroFrontends
๋ฅผ ์ฌ์ฉํ ๋ ์ ์ ํ๊ฒ ๊ฒฉ๋ฆฌํ ์ ์๊ฒ ํ์ต๋๋ค. - ํ์ง๋ง, ๋ค๋ค ์๋ค์ถ์ด
context
๋ ๋ฆฌ์กํธ์์๋ง ์ฌ์ฉ ๊ฐ๋ฅํ ๊ธฐ๋ฅ์ ๋๋ค.context
๋ que`ryClient์ ๋ํ ์ ๊ทผ ๊ถํ์ ์ฃผ๋ ์ญํ ์ ํ ๋ฟ์ ๋๋ค. - v5์์๋ ์์ ๋์ผํ ๊ธฐ๋ฅ์ ์๋ ์์ ์ฒ๋ผ ์ปค์คํ
queyClient๋ฅผ
์ง์
์ ๋ฌํจ์ผ๋ก์จ ํด๊ฒฐํ์ต๋๋ค. ์ด์ ๋ ์ด๋ค ๋ค๋ฅธ ํ๋ ์์ํฌ์ ๊ตฌ์ ๋ฐ์ง ์๊ณ ๋์ผํ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
import { queryClient } from './my-client'
const { data } = useQuery(
{
queryKey: ['users', id],
queryFn: () => fetch(...),
- context: customContext
},
+ queryClient,
)
maxPages
๋ฅผ ์ํดrefetchpage
๋ฅผ ์ ๊ฑฐํ์์ต๋๋ค.- v4์์๋
refetchPage
ํจ์๋ฅผ ์ฌ์ฉํ์ฌ infinite queries์ ๋ํด refresh ํ ํ์ด์ง๋ฅผ ์ ์ํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ต๋๋ค. - ๊ทธ๋ฌ๋ ๋ชจ๋ ํ์ด์ง๋ฅผ refresh ํ๋ฉด UI ๋ถ์ผ์น๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ๋ํ ์ด ์ต์
์
queryClient.refetchQueries
์์ ์ฌ์ฉํ ์ ์์ง๋งnomal queries
๊ฐ ์๋infinite queries
์๋ํด์๋ง ๋์ํฉ๋๋ค. - v5์์๋
query data
๋ฅผ ์ ์ฅํ๊ณ , ๋ค์ ๊ฐ์ ธ์ฌ ์ ์๋ ํ์ด์ง ์๋ฅผ ์ ํํ๋infinite queries
๋ฅผ ์ํ ์๋ก์ดmaxPages
์ต์ ์ด ํฌํจ๋์ด ์์ต๋๋ค.
useInfiniteQuery({
queryKey: ["projects"],
queryFn: fetchProjects,
initialPageParam: 0,
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor,
maxPages: 3,
});
infinite queries
๋ ๋ง์ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ฌ์๋ก ๋ ๋ง์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉฐ, ๋ชจ๋ ํ์ด์ง๋ฅผ ์์ฐจ์ ์ผ๋ก ๋ค์ ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์query refetching
ํ๋ก์ธ์ค๋ ๋๋ ค์ง๋๋ค.maxPages
๋ฅผ ํ์ฉํ๋ฉด ํ์ด์ง ์๋ฅผ ์ ํํ๊ณ ์ดํ์ ๋ค์ ๊ฐ์ ธ์ฌ ์ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ๋จ์ ์ ๋ณด์ํ ์ ์์ต๋๋ค. ์ฐธ๊ณ ๋กinfinite list
๋์๋ฐฉํฅ
์ด์ฌ์ผ ํ๊ธฐ ๋๋ฌธ์ ์ ์์ ์ฒ๋ผgetNextPageParam
๊ณผgetPreviousPageParam
์ ๋ชจ๋ ์ ์ํด์ผ ํ๋ค๋ ์ ์ ์ฃผ์ํด์ผ ํฉ๋๋ค.
- ์ด์ ์๋
undefined
๊ฐ์ ๊ฐ์งpageParam
์queryFn
์ ์ ๋ฌํ๊ณ ,queryFn
์์pageParam
์ ๋ํ ๊ธฐ๋ณธ ๊ฐ์ ์ ์ํ์ต๋๋ค. ํ์ง๋ง ์ด๋ฐ ๊ฒฝ์ฐ ์ง๋ ฌํ ํ ์ ์๋ ์ฟผ๋ฆฌ ์บ์์undefined
์ธ ์ํ๋ก ์ ์ฅ๋๋ค๋ ๋จ์ ์ด ์์ต๋๋ค. - v5๋ถํฐ๋ ์๋ ์์ ์ฒ๋ผ
infinite Query
์ต์ ์ ๋ช ์์ ์ธinitialPageParam
์ ์ ๋ฌํด์ผ ํฉ๋๋ค.
useInfiniteQuery({
queryKey,
- queryFn: ({ pageParam = 0 }) => fetchSomething(pageParam),
+ queryFn: ({ pageParam }) => fetchSomething(pageParam),
+ initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.next,
})
- ์ด์ ์๋ ์๋ ์์ ์ฒ๋ผ
pageParams
๊ฐ์ ์๋์ ์ผ๋กfetchNextPage
๋๋fetchPreviousPage
์ ์ง์ ์ ๋ฌํ์ฌgetNextPageParam
๋๋getPreviousPageParam
์์ ๋ฐํ๋๋pageParam
๋ฅผ ๋ฎ์ด์ฐ๋ ๊ฒ์ด ํ์ฉ๋์์ต๋๋ค.
// v4
function Projects() {
const fetchProjects = ({ pageParam = 0 }) =>
fetch("/api/projects?cursor=" + pageParam);
const {
status,
data,
isFetching,
isFetchingNextPage,
fetchNextPage,
hasNextPage,
} = useInfiniteQuery({
queryKey: ["projects"],
queryFn: fetchProjects,
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
});
// Pass your own page param
const skipToCursor50 = () => fetchNextPage({ pageParam: 50 });
}
- ํ์ง๋ง ์ด
pageParam
์ ๋ฎ์ด์ฐ๋ ๊ธฐ๋ฅ์refetch
์์๋ ์ ํ ์๋ํ์ง ์์๊ณ , ๋ง์ ์ฌ๋๋ค์ด ์ฌ์ฉํ๋ ๊ธฐ๋ฅ์ด ์๋์์ต๋๋ค. ์ฆ,infinite queries
์์getNextPageParam
์ด ํ์์ ์์ ์๋ฏธํฉ๋๋ค.
15. โญ๏ธ Returning 'null' from 'getNextPageParam' or 'getPreviousPageParam' now indicates that there is no further page available
- v4์์๋ ๋ ์ด์ ํ์ด์ง ์์์ ๋ํ๋ด๊ธฐ ์ํด ๋ช
์์ ์ผ๋ก
undefined
๋ฅผ ๋ฐํํด์ผ ํ์ต๋๋ค. v5๋ถํฐ๋undefined
๋ฟ๋ง ์๋๋ผnull
๊น์ง ํฌํจํ๋๋ก ํ์ฅ๋์ต๋๋ค.
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
TPageParam | undefined | null;
getPreviousPageParam: (firstPage, allPages, firstPageParam, allPageParams) =>
TPageParam | undefined | null;
- ์๋ฒ์์ retry์ ๊ธฐ๋ณธ ๊ฐ์
3
์ด ์๋0
์ ๋๋ค. prefetching
์ ๊ฒฝ์ฐ ํญ์ ๊ธฐ๋ณธ๊ฐ์ด 0์ด์์ง๋ง,suspense
๊ฐ ํ์ฑํ๋ ์ฟผ๋ฆฌ๋ ์ด์ ์๋ฒ์์๋ ์ง์ ์คํํ ์ ์๊ธฐ ๋๋ฌธ์(React v18 ์ดํ) ์๋ฒ์์ ์ฌ์๋๋ฅผ ์ ํ ํ์ง ์๋๋ก ํด์ผํฉ๋๋ค.
17. โญ๏ธ 'status: loading' has been changed to 'status: pending' and 'isLoading' has been changed to 'isPending' and 'isInitialLoading' has now been renamed to 'isLoading'
loading
์ต์ ์ดpending
์ผ๋ก ๋ณ๊ฒฝ๋์์ผ๋ฉฐ, ๋ง์ฐฌ๊ฐ์ง๋กisLoading
ํ๋๊ทธ๊ฐisPending
์ผ๋ก ๋ณ๊ฒฝ๋์์ต๋๋ค.
isPending: boolean;
// A derived boolean from the status variable above, provided for convenience.
isSuccess: boolean;
// A derived boolean from the status variable above, provided for convenience.
isError: boolean;
// A derived boolean from the status variable above, provided for convenience.
mutation
์ ๊ฒฝ์ฐ์๋isLoading
ํ๋๊ทธ๊ฐisPending
์ผ๋ก ๋ณ๊ฒฝ๋์์ต๋๋ค.
status: string;
/*
Will be:
- 'idle' initial status prior to the mutation function executing.
- 'pending' if the mutation is currently executing.
- 'error' if the last mutation attempt resulted in an error.
- 'success' if the last mutation attempt was successful.
'isIdle', 'isPending', 'isSuccess', 'isError': boolean variables derived from 'status'
*/
- ๊ทธ๋ฆฌ๊ณ
isPending && isFetching
์ผ๋ก ๊ตฌํ๋๋ ์๋ก์ดisLoading
ํ๋๊ทธ๊ฐ ์ถ๊ฐ๋์์ต๋๋ค. - ์ด๋ ๊ธฐ์กด์
isInitialLoading
๊ณผ ๋์ผํ ๊ธฐ๋ฅ์ ํ๋๋ฐ,isInitialLoading
์ ๋ ์ด์ ์ฌ์ฉ๋์ง ์์ผ๋ฉฐ ๋ค์ ๋ฉ์ด์ ๋ฒ์ ์ ๋ฐ์ดํธ์์ ์ ๊ฑฐ๋ ์์ ์ ๋๋ค.
- v5๋ถํฐ๋ ๋๊ด์ ์ ๋ฐ์ดํธ๋ฅผ ์ํํ๋ ๋จ์ํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
const queryInfo = useTodos();
const addTodoMutation = useMutation({
mutationFn: (newTodo: string) => axios.post("/api/data", { text: newTodo }),
onSettled: () => queryClient.invalidateQueries({ queryKey: ["todos"] }),
});
if (queryInfo.data) {
return (
<ul>
{queryInfo.data.items.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
{addTodoMutation.isPending && (
<li key={String(addTodoMutation.submittedAt)} style={{ opacity: 0.5 }}>
{addTodoMutation.variables}
</li>
)}
</ul>
);
}
- ์ ์์ ์์๋ ๋ฐ์ดํฐ๋ฅผ ์บ์์ ์ง์ ์ฐ๋ ๋์ ์
mutation
์ด ์คํ์ค์ผ ๋ UI๊ฐ ํ์๋๋ ๋ฐฉ์๋ง ๋ณ๊ฒฝํฉ๋๋ค. ํด๋น ๋ฐฉ๋ฒ์๋๊ด์ ์ ๋ฐ์ดํธ
๋ฅผ ํ์ํด์ผ ํ๋ ์์น๊ฐ ํ ๊ณณ๋ง ์๋ ๊ฒฝ์ฐ์ ํจ๊ณผ์ ์ ๋๋ค. ๋๊ด์ ์ ๋ฐ์ดํธ
์ ๊ด๋ จ๋ ์์ธํ ๋ด์ฉ์ optimistic-updates๋ฅผ ์ฐธ๊ณ ํด์ฃผ์๊ธธ ๋ฐ๋๋๋ค.
- ์ด์
infinite queries
๋normal queries
์ฒ๋ผprefetch
ํ ์ ์์ต๋๋ค. - ๊ธฐ๋ณธ์ ์ผ๋ก
query
์ ์ฒซ ๋ฒ์งธ ํ์ด์ง๋งprefetch
๋๋ฉฐ ์ง์ ๋queryKey
์๋์ ์ ์ฅ๋ฉ๋๋ค. ๋ ๊ฐ ์ด์์ ํ์ด์ง๋ฅผ ๋ฏธ๋ฆฌ ๊ฐ์ ธ์ค๋ ค๋ฉดpages
์ต์ ์ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค. prefetch
์ ๊ด๋ จ๋ ์์ธํ ๋ด์ฉ์ prefetching๋ฅผ ์ฐธ๊ณ ํด์ฃผ์๊ธธ ๋ฐ๋๋๋ค.
const prefetchTodos = async () => {
// The results of this query will be cached like a normal query
await queryClient.prefetchInfiniteQuery({
queryKey: ["projects"],
queryFn: fetchProjects,
initialPageParam: 0,
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
pages: 3, // prefetch the first 3 pages
});
};
useQueries
๊ฒฐ๊ณผ ๋ฐ์ดํฐ๋ฅผ ๋จ์ผ ๊ฐ์ผ๋ก ๊ฒฐํฉํ๋ ค๋ฉดcombine
์ต์ ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
const ids = [1,2,3]
const combinedQueries = useQueries({
queries: ids.map(id => (
{ queryKey: ['post', id], queryFn: () => fetchPost(id) },
)),
combine: (results) => {
return ({
data: results.map(result => result.data),
pending: results.some(result => result.isPending),
})
}
})
- ์ ์์ ์์๋
combinedQueries
๋data
์pending
์์ฑ์ด ์๋ ๊ฐ์ฒด๊ฐ ๋ฉ๋๋ค. ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์ ๋ค๋ฅธ ๋ชจ๋ ์์ฑ์ ์์ค๋๋ค๋ ์ ์ ์ฃผ์ํด์ผ ๋ฉ๋๋ค.
- v5์์๋
data fetching
์ ๋ํsuspense
๊ฐ ๋ง์นจ๋ด ์์ ํ๋์์ต๋๋ค. useSuspenseQuery
,useSuspenseInfiniteQuery
,useSuspenseQueries
3๊ฐ์ง ํ ์ด ์ถ๊ฐ๋์์ต๋๋ค.- ์ 3๊ฐ์ง ํ
์ ์ฌ์ฉํ๊ฒ ๋๋ฉด ํ์
๋ ๋ฒจ์์
data
๊ฐundefined
์ํ๊ฐ ๋์ง ์์ต๋๋ค.
import { useSuspenseQuery } from "@tanstack/react-query";
const { data } = useSuspenseQuery({ queryKey, queryFn });
- suspense์ ๊ด๋ จ๋ ์์ธํ ๋ด์ฉ์ suspense๋ฅผ ์ฐธ๊ณ ํด์ฃผ์๊ธธ ๋ฐ๋๋๋ค.
- Tanstack Query v5๋ ํ์ํ TypeScript ์ต์ ๋ฒ์ ์ด
v4.7
์ ๋๋ค.
- Tanstack Query v5๋ ํ์ํ React ์ต์ ๋ฒ์ ์ด
v18.0
์ ๋๋ค. ์ด๋ React v18 ์ด์์์๋ง ์ฌ์ฉํ ์ ์๋useSyncExternalStore
ํ ์ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
Chrome >= 91
Firefox >= 90
Edge >= 91
Safari >= 15
iOS >= 15
opera >= 77