본문으로 건너뛰기

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),
}
},
})

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

메모이제이션​

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

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

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

TypeScript: select 옵션의 타입 지정​

useQuery와 달리, useQueries는 형제 queryFn에서 인라인 select의 data 인수를 추론할 수 없습니다. 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에도 동일하게 적용됩니다.