본문으로 건너뛰기

useQueries

useQueries 훅을 사용하여 가변 개수의 쿼리를 가져올 수 있습니다:

const ids = [1, 2, 3]
const results = useQueries({
queries: ids.map((id) => ({
queryKey: ['post', id],
queryFn: () => fetchPost(id),
staleTime: Infinity,
})),
})

옵션

useQueries 훅은 queries 키가 있는 옵션 객체를 받으며, 이 키의 값은 useQuery과 동일한 쿼리 옵션 객체의 배열입니다(queryClient 옵션 제외 - QueryClient는 최상위 수준에서 전달할 수 있기 때문입니다).

  • queryClient?: QueryClient
    • 사용자 지정 QueryClient를 제공하려면 이를 사용합니다. 그렇지 않으면 가장 가까운 컨텍스트의 항목이 사용됩니다.
  • combine?: (result: UseQueriesResults) => TCombinedResult
    • 이를 사용하여 쿼리 결과를 단일 값으로 결합합니다.

쿼리 객체 배열에 동일한 쿼리 키가 두 번 이상 있으면 쿼리 간에 일부 데이터가 공유될 수 있습니다. 이를 방지하려면 쿼리의 중복을 제거하고 결과를 원하는 구조로 다시 매핑하는 것을 고려하세요.

placeholderData

placeholderData 옵션은 useQueries에도 있지만, useQueries의 입력에 포함되는 Query 수는 렌더링할 때마다 달라질 수 있으므로 useQuery와 달리 이전에 렌더링된 Query에서 전달된 정보를 받지 않습니다.

반환값

useQueries 훅은 모든 쿼리 결과가 담긴 배열을 반환합니다. 반환되는 순서는 입력 순서와 같습니다.

결합

결과의 data(또는 기타 Query 정보)을 하나의 값으로 결합하려면 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),
}
},
})

위 예제에서 combinedQueriesdata 속성과 pending 속성을 가진 객체입니다. Query 결과의 다른 모든 속성은 유실된다는 점에 유의하세요.

메모이제이션

combine 함수는 다음 경우에만 다시 실행됩니다:

  • combine 함수 자체의 참조가 변경되었습니다
  • 쿼리 결과 중 하나라도 변경됨

이는 위에 표시된 것처럼 인라인된 combine 함수가 렌더링할 때마다 실행된다는 의미입니다. 이를 방지하려면 combine 함수를 useCallback으로 감싸거나, 의존성이 없다면 안정적인 함수 참조로 추출할 수 있습니다.

TypeScript: select 옵션의 타입 지정

useQuery와 달리, useQueries는 형제 queryFn에서 인라인 selectdata 인수를 추론할 수 없습니다. useQueries는 전체 queries 배열의 타입을 한 번에 추론하므로, 인라인으로 작성된 쿼리 객체의 select 매개변수는 동일한 객체의 queryFn에서 문맥적으로 타입이 지정될 수 없어 unknown으로 대체됩니다. 이는 알려진 TypeScript 제한 사항입니다.

useQueries({
queries: [
{
queryKey: ['post', 1],
queryFn: () => fetchPost(1),
// ❌ `data` is `unknown` here
select: (data) => data.title,
},
],
})

지원되는 해결 방법은 두 가지입니다:

  1. select 매개변수를 명시적으로 표기합니다:
useQueries({
queries: [
{
queryKey: ['post', 1],
queryFn: () => fetchPost(1),
// ✅ `data` is `Post`
select: (data: Post) => data.title,
},
],
})
  1. useQueries에 도달하기 전에 단일 객체에서 타입을 이행하는 queryOptions 헬퍼로 쿼리를 정의합니다:
const postOptions = (id: number) =>
queryOptions({
queryKey: ['post', id],
queryFn: () => fetchPost(id),
// ✅ `data` is `Post`
select: (data) => data.title,
})

useQueries({ queries: [postOptions(1), postOptions(2)] })

queryOptions 결과를 펼쳐 인라인에서 해당 select를 재정의할 때도 같은 제한이 적용됩니다. 재정의하는 select는 여전히 unknown으로 대체됩니다:

useQueries({
queries: [
{
...postOptions(1),
// ❌ `data` is `unknown` here
select: (data) => data.title,
},
],
})

재정의가 useQueries에 도달하기 전에 처리되도록 spread를 다시 queryOptions로 래핑합니다:

useQueries({
queries: [
queryOptions({
...postOptions(1),
// ✅ `data` is `Post`
select: (data) => data.title,
}),
],
})

useSuspenseQueries에도 동일하게 적용됩니다.