기본적으로 TanStack Query는 공격적이지만 정상적인 기본값으로 구성되어 있습니다. 이러한 기본값은 새로운 사용자가 방심하고 있거나 사용자에 의해 인식되지 않은 경우에 학습/디버그를 어렵게 할 수 있습니다. TanStack Query를 학습하고 계속 사용할 때 다음 사항을 주의하십시오:
- useQuery 또는 useInfinite를 사용하여 인스턴스를 조회하는 기본적으로 쿼리는 캐시된 데이터를 data as stale(오래된 데이터)로 간주합니다.
이 동작을 변경하려면
staleTime
옵션을 사용하여 쿼리를 전역 및 쿼리별로 구성할 수 있습니다. 더 긴staleTime
을 지정하면 쿼리가 자주 데이터를 다시 가져오지 않음을 의미합니다
이 기능을 변경하려면
refetchOnMount
,refetchOnWindowFocus
,refetchOnReconnect
및refetchInterval
과 같은 옵션을 사용할 수 있습니다
useQuery
, useInfiniteQuery
또는 쿼리 옵저버의 활성 인스턴스가 더 이상 없는 쿼리 결과는 "inactive(비활성)"으로 레이블이 지정되고 나중에 다시 사용될 경우 캐시에 남아 있습니다.이를 변경하려면 쿼리의 기본 gcTime을 1000 * 60 * 5밀리초가 아닌 다른 gcTime으로 변경할 수 있습니다.
이를 변경하려면 기본 "retry(재시도)" 및 "retryDelay(재시도 쿼리에 대한 지연 옵션)"을 3이 아닌 다른 항목으로 변경하고 기본 지수 백오프 기능을 변경할 수 있습니다.
구조 공유는 "JSON-compatible values(JSON 호환 값)"에서만 작동하므로 다른 값 유형은 항상 변경된 것으로 간주됩니다. 예를 들어, 응답이 크기 때문에 성능 문제가 발생하는 경우 "config.structuralSharing" 플래그를 사용하여 이 기능을 사용하지 않도록 설정할 수 있습니다. 쿼리 응답에서 JSON 호환되지 않는 값을 다루면서도 데이터가 변경되었는지 여부를 여전히 감지하고 싶다면 "config.structuralSharing" 으로 사용자 지정 기능을 제공하여 필요에 따라 참조를 유지할 수 있습니다.
더 읽기
기본에 대한 자세한 내용은 커뮤니티 리소스의 다음 기사를 참조하십시오:
쿼리는 unique key(고유 키) 에 연결된 비동기 데이터 소스에 대한 선언적 종속성입니다. 쿼리는 서버에서 데이터를 가져오기 위해 모든 약속 기반 메서드(GET 및 POST 메서드 포함)와 함께 사용할 수 있습니다. 메서드가 서버의 데이터를 수정하는 경우 대신 Mutations을 사용하는 것이 좋습니다.
컴포넌트 또는 사용자 커스텀 후크에서 쿼리를 구독하려면 적어도 다음과 함께 useQuery
후크를 호출합니다:
import { useQuery } from '@tanstack/react-query'
function App() {
const info = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList })
}
제공하는 고유 키는 애플리케이션 전체에서 쿼리를 리페칭, 캐싱 및 공유하는 데 내부적으로 사용됩니다.
useQuery
를 사용하여 반환된 쿼리 결과에는 템플릿 작성에 필요한 쿼리 및 기타 데이터 사용에 대한 모든 정보가 포함됩니다:
const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList })
result
Object(결과 객체)에는 생산성을 높이기 위해 알아야 할 몇 가지 매우 중요한 상태가 포함되어 있습니다. 쿼리는 특정 순간에만 다음 상태 중 하나에 있을 수 있습니다:
isPending
or status === 'pending'
- 쿼리에 아직 데이터가 없습니다isError
or status === 'error'
- 쿼리에 오류가 발생했습니다isSuccess
or status === 'success'
- 쿼리가 성공했으며 데이터를 사용할 수 있습니다이러한 기본 상태 외에도 쿼리 상태에 따라 더 많은 정보를 사용할 수 있습니다:
error
- 쿼리가 isError
상태인 경우 오류 속성을 통해 오류를 사용할 수 있습니다.data
- 쿼리가 isSuccess
상태이면 데이터 속성을 통해 데이터를 사용할 수 있습니다.isFetching
- 임의의 상태에서 쿼리가 임의의 시간(백그라운드 리페칭 포함)에 페칭 중이면 isFetching
이 true 입니다.대부분의 쿼리에서 isPending
상태를 확인한 다음 isError
상태를 확인하고 마지막으로 데이터를 사용할 수 있다고 가정하고 성공적인 상태를 렌더링하면 됩니다:
function Todos() {
const { isPending, isError, data, error } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodoList,
})
if (isPending) {
return <span>Loading...</span>
}
if (isError) {
return <span>Error: {error.message}</span>
}
// We can assume by this point that `isSuccess === true`
return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}
Boolean 방식이 마음에 안든다면 언제든지 status
상태를 사용할 수 있습니다:
function Todos() {
const { status, data, error } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodoList,
})
if (status === 'pending') {
return <span>Loading...</span>
}
if (status === 'error') {
return <span>Error: {error.message}</span>
}
// also status === 'success', but "else" logic works, too
return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}
TypeScript는 또한 데이터에 액세스하기 전에 보류 및 오류를 확인한 경우 데이터 유형을 올바르게 좁힙니다.
상태 필드 외에 다음 옵션과 함께 추가 fetchStatus
속성도 얻을 수 있습니다:
fetchStatus === 'fetching'
- 현재 쿼리를 가져오는 중입니다.fetchStatus === 'paused'
- 쿼리가 가져오기를 원했지만 일시 중지되었습니다. 이에 대한 자세한 내용은 네트워크 모드 가이드에서 확인하십시오.fetchStatus === 'idle'
- 쿼리가 현재 아무 동작도 하지 않습니다.백그라운드 리페치와 오래된 재검증 로직을 사용하면 status
및 fetchStatus
에 대한 모든 조합이 가능합니다. 예를 들어 다음과 같습니다:
success
상태의 FetchStatua 쿼리는 보통 idle
fetchStatus에 있지만 백그라운드 리페치가 발생하는 경우 fetch
에 있을 수도 있습니다.pending
status와 fetching
fetchStatus에 있지만 네트워크 연결이 없는 경우 일시 중지될 수도 있습니다따라서 실제로 데이터를 가져오지 않아도 쿼리가 pending
상태에 있을 수 있습니다. 원칙적으로 다음과 같습니다:
상태는 데이터에 대한 정보를 제공합니다. 데이터가 있습니까, 없습니까?
fetchStatus는 쿼리 Fn에 대한 정보를 제공합니다. 실행 중입니까, 실행 중입니까?
status
는 데이터에 대한 정보를 제공합니다. 데이터가 있습니까, 없습니까?fetchStatus
는 queryFn
에 대한 정보를 제공합니다. 실행 중입니까, 실행 중입니까?더 읽기
상태를 확인하는 다른 방법을 보려면 커뮤니티 리소스를 살펴보십시오.
핵심은 TanStack Query가 쿼리 키를 기반으로 쿼리 캐싱을 관리하는 것입니다. 쿼리 키는 최상위 수준의 Array여야 하며, 단일 문자열이 있는 Array처럼 단순하거나 많은 문자열과 중첩 개체가 있는 Array처럼 복잡할 수 있습니다. 쿼리 키가 직렬화 가능하고 쿼리의 데이터에 고유한 경우 사용할 수 있습니다!
키의 가장 간단한 형태는 상수 값을 갖는 배열입니다. 이 형식은 다음에 유용합니다:
// A list of todos
useQuery({ queryKey: ['todos'], ... })
// Something else, whatever!
useQuery({ queryKey: ['something', 'special'], ... })
쿼리가 데이터를 고유하게 설명하기 위해 더 많은 정보를 필요로 할 때 문자열이 포함된 배열과 일련화 가능한 개체 수를 사용하여 설명할 수 있습니다. 이는 다음과 같은 경우에 유용합니다:
// An individual todo
useQuery({ queryKey: ['todo', 5], ... })
// An individual todo in a "preview" format
useQuery({ queryKey: ['todo', 5, { preview: true }], ...})
// A list of todos that are "done"
useQuery({ queryKey: ['todos', { type: 'done' }], ... })
이는 개체의 키 순서에 관계없이 다음 쿼리가 모두 동일한 것으로 간주됨을 의미합니다:
useQuery({ queryKey: ['todos', { status, page }], ... })
useQuery({ queryKey: ['todos', { page, status }], ...})
useQuery({ queryKey: ['todos', { page, status, other: undefined }], ... })
그러나 다음 쿼리 키는 동일하지 않습니다. 배열 항목 순서가 중요합니다!
useQuery({ queryKey: ['todos', status, page], ... })
useQuery({ queryKey: ['todos', page, status], ...})
useQuery({ queryKey: ['todos', undefined, page, status], ...})
쿼리 키는 가져오는 데이터를 고유하게 설명하기 때문에 쿼리 함수에서 사용하는 변수 중에서 변경되는 변수를 포함해야 합니다. 예를 들어 다음과 같습니다:
function Todos({ todoId }) {
const result = useQuery({
queryKey: ['todos', todoId],
queryFn: () => fetchTodoById(todoId),
})
}
쿼리 키는 쿼리 함수에 대한 종속성 역할을 합니다. 쿼리 키에 종속 변수를 추가하면 쿼리가 독립적으로 캐시되고 변수가 변경될 때마다 쿼리가 자동으로 다시 붙여집니다(오래된 시간 설정에 따라 다름). 자세한 내용과 예제는 exhaustive-deps 섹션을 참조하십시오.
더 읽기
더 큰 응용 프로그램에서 쿼리 키를 구성하는 방법에 대한 팁은 Effective React Query Keys를 살펴보고 커뮤니티 리소스에서 Query Key Factory Package를 확인하십시오.
쿼리 함수는 말 그대로 Promise를 반환하는 모든 함수가 될 수 있습니다. 반환되는 Promise는 데이터를 해결하거나 오류를 던져야 합니다.
다음은 모두 유효한 쿼리 함수 구성입니다:
useQuery({ queryKey: ['todos'], queryFn: fetchAllTodos })
useQuery({ queryKey: ['todos', todoId], queryFn: () => fetchTodoById(todoId) })
useQuery({
queryKey: ['todos', todoId],
queryFn: async () => {
const data = await fetchTodoById(todoId)
return data
},
})
useQuery({
queryKey: ['todos', todoId],
queryFn: ({ queryKey }) => fetchTodoById(queryKey[1]),
})
TanStack Query가 쿼리에 오류가 발생했음을 확인하려면 쿼리 함수가 거부된 Promise를 던지거나 반환해야 합니다. 쿼리 함수에서 발생하는 모든 오류는 쿼리의 오류 상태에 그대로 유지됩니다.
const { error } = useQuery({
queryKey: ['todos', todoId],
queryFn: async () => {
if (somethingGoesWrong) {
throw new Error('Oh no!')
}
if (somethingElseGoesWrong) {
return Promise.reject(new Error('Oh no!'))
}
return data
},
})
axios
나 graphql-request
와 같은 대부분의 유틸리티는 HTTP 호출에 실패하면 자동으로 오류를 발생시키지만, fetch
와 같은 일부 유틸리티는 기본적으로 오류를 발생시키지 않습니다. 그런 경우에는 스스로 오류를 발생시킬 필요가 있습니다. 일반적인 fetch
API를 사용하여 간단한 방법은 다음과 같습니다:
useQuery({
queryKey: ['todos', todoId],
queryFn: async () => {
const response = await fetch('/todos/' + todoId)
if (!response.ok) {
throw new Error('Network response was not ok')
}
return response.json()
},
})
쿼리 키는 가져오는 데이터를 고유하게 식별하기 위한 것일 뿐만 아니라 쿼리 기능 컨텍스트의 일부로 쿼리 기능에 편리하게 전달됩니다. 항상 필요한 것은 아니지만 필요한 경우 쿼리 기능을 추출할 수 있습니다:
function Todos({ status, page }) {
const result = useQuery({
queryKey: ['todos', { status, page }],
queryFn: fetchTodoList,
})
}
// Access the key, status and page variables in your query function!
function fetchTodoList({ queryKey }) {
const [_key, { status, page }] = queryKey
return new Promise()
}
QueryFunctionContext
는 각 쿼리 함수에 전달되는 객체입니다. 다음으로 구성됩니다:
queryKey: QueryKey
: 쿼리 키signal?: AbortSignal
meta: Record<string, unknown> | undefined
추가적으로, 무한 쿼리(Infinite Queries)에는 다음 옵션이 전달됩니다:
pageParam: TPageParam
direction: 'forward' | 'backward'
쿼리 키(queryKey)와 쿼리 함수(queryFn)를 여러곳에서 공유하면서도 서로 가까이 유지하는 가장 좋은 방법 중 하나는 queryOptions 헬퍼를 사용하는 것입니다. 이 헬퍼는 런타임 시에 단순히 전달된 값을 반환하지만, TypeScript와 함께 사용할 때 많은 이점이 있습니다. 한 곳에서 쿼리에 대한 모든 가능한 옵션을 정의할 수 있으며, 모든 옵션에 대해 타입 추론과 타입 안전성을 얻을 수 있습니다.
import { queryOptions } from '@tanstack/react-query'
function groupOptions(id: number) {
return queryOptions({
queryKey: ['groups', id],
queryFn: () => fetchGroups(id),
staleTime: 5 * 1000,
})
}
// usage:
useQuery(groupOptions(1))
useSuspenseQuery(groupOptions(5))
useQueries({
queries: [groupOptions(1), groupOptions(2)],
})
queryClient.prefetchQuery(groupOptions(23))
queryClient.setQueryData(groupOptions(42).queryKey, newGroups)
Infinite Queries(무한 쿼리)의 경우, 별도의 infiniteQueryOptions
헬퍼를 사용할 수 있습니다.
TanStack Query는 네트워크 연결이 없는 경우 쿼리와 뮤테이션이 어떻게 동작해야 하는지를 구별하기 위해 세 가지 네트워크 모드를 제공합니다. 이 모드는 각 쿼리 및 뮤테이션에 대해 개별적으로 설정할 수 있으며, 또는 쿼리/뮤테이션 기본값을 통해 전역적으로 설정할 수 있습니다.
TanStack Query는 주로 데이터 페칭 라이브러리와 결합하여 데이터 페칭에 사용되기 때문에 기본 네트워크 모드는 온라인입니다.
이 모드에서는 네트워크 연결이 없으면 쿼리와 뮤테이션이 실행되지 않습니다. 이것이 기본 모드입니다. 쿼리에 대한 페치가 시작되면 네트워크 연결이 없어서 페치를 할 수 없는 경우, 해당 쿼리는 현재 상태(pending - 대기 중
, error - 오류
, success - 성공
)를 유지합니다. 그러나 추가적으로 fetchStatus가 노출됩니다. 이 상태는 다음과 같습니다:
fetching
: queryFn
이 실제로 실행 중이며 요청이 진행 중입니다.paused
: 쿼리가 실행되지 않으며 네트워크 연결이 다시 될 때까지 일시 중지된 상태입니다.idle
: 쿼리가 페치되지 않고 있으며 일시 중지도 아닙니다.isFetching
과 isPaused
플래그는 이 상태에서 파생되어 편의를 위해 노출됩니다.
로딩 스피너를 표시하려면 단순히 대기 상태(pending)를 확인하는 것으로는 충분하지 않을 수 있습니다. 쿼리가 처음 마운트될 때 네트워크 연결이 없으면 상태는 pending이지만 fetchStatus는 paused일 수 있습니다.
만약 온라인 상태에서 쿼리가 실행 중인데 페치가 진행되는 동안 오프라인 상태가 되면, TanStack Query는 재시도 메커니즘을 일시 중지합니다. 일시 중지된 쿼리는 네트워크 연결이 다시 될 때 계속 실행됩니다. 이는 refetchOnReconnect(기본값 true)와는 독립적입니다. 왜냐하면 이는 다시 페치가 아니라 단순히 계속 실행되기 때문입니다. 쿼리가 그 동안 취소된 경우에는 계속 실행되지 않습니다.
이 모드에서는 TanStack Query가 항상 페치를 수행하며, 온라인/오프라인 상태를 무시합니다. 이는 쿼리가 작동하기 위해 활성 네트워크 연결이 필요 없는 환경에서 TanStack Query를 사용하는 경우에 선택할 수 있는 모드입니다. 예를 들어, AsyncStorage에서 읽기만 하거나 queryFn에서 단순히 Promise.resolve(5)를 반환하고 싶은 경우입니다.
이 모드는 앞서 설명한 두 가지 모드(Online 및 Always) 사이의 중간 지점에 위치합니다. TanStack Query는 queryFn을 한 번 실행하지만, 그 이후에는 재시도를 일시 중지합니다. 이는 오프라인 우선 PWA에서 서비스 워커가 요청을 가로채어 캐싱하는 경우나, Cache-Control 헤더를 통해 HTTP 캐싱을 사용하는 경우에 매우 유용합니다.
TanStack Query Devtools는 네트워크 연결이 없는 상태에서 쿼리가 페치 중일 때 쿼리를 일시 중지된 상태로 표시합니다. 또한, 오프라인 동작을 모의(Mock)하는 토글 버튼도 제공됩니다. 이 버튼은 실제 네트워크 연결을 변경하지는 않지만, OnlineManager를 오프라인 상태로 설정합니다. 실제 네트워크 연결을 변경하려면 브라우저 개발자 도구를 사용해야 합니다.
"Parallel" queries는 여러 쿼리를 동시에 실행하여 페칭의 동시성을 극대화하는 방식입니다. 쿼리가 동시에 실행되어 결과를 더 빨리 받을 수 있습니다.
병렬 쿼리의 수가 변경되지 않는 경우, 병렬 쿼리를 사용하는 데 별도의 추가 작업이 필요하지 않습니다. TanStack Query의 useQuery와 useInfiniteQuery 훅을 나란히 사용하면 됩니다.
function App () {
// The following queries will execute in parallel
const usersQuery = useQuery({ queryKey: ['users'], queryFn: fetchUsers })
const teamsQuery = useQuery({ queryKey: ['teams'], queryFn: fetchTeams })
const projectsQuery = useQuery({ queryKey: ['projects'], queryFn: fetchProjects })
...
}
React Query를 서스펜스 모드로 사용하는 경우, 병렬 쿼리의 패턴이 제대로 작동하지 않습니다. 첫 번째 쿼리가 내부적으로 프라미스를 던져 컴포넌트를 서스펜드하게 되면, 다른 쿼리들이 실행되기 전에 컴포넌트가 일시 중지됩니다. 이를 해결하기 위해서는
useSuspenseQueries
훅을 사용하거나 각useSuspenseQuery
인스턴스에 대해 별도의 컴포넌트를 만들어 병렬 처리를 구현해야 합니다.
useQueries
TanStack Query의 useQueries 훅은 렌더링마다 실행해야 할 쿼리 수가 변경되는 경우에 유용합니다. 수동으로 쿼리를 관리하면 훅의 규칙을 위반할 수 있기 때문에, useQueries
를 사용하여 동적으로 여러 쿼리를 병렬로 실행할 수 있습니다.
useQueries
는 쿼리 객체 배열을 값으로 가지는 queries 키가 있는 옵션 객체를 받아들이며, 쿼리 결과의 배열을 반환합니다.
function App({ users }) {
const userQueries = useQueries({
queries: users.map((user) => {
return {
queryKey: ['user', user.id],
queryFn: () => fetchUserById(user.id),
}
}),
})
}
종속(또는 직렬) 쿼리는 이전 쿼리가 완료된 후에 실행할 수 있습니다. 이를 위해 enabled
옵션을 사용하여 쿼리가 실행 준비가 되었음을 알릴 수 있습니다:
// Get the user
const { data: user } = useQuery({
queryKey: ['user', email],
queryFn: getUserByEmail,
})
const userId = user?.id
// Then get the user's projects
const {
status,
fetchStatus,
data: projects,
} = useQuery({
queryKey: ['projects', userId],
queryFn: getProjectsByUser,
// The query will not execute until the userId exists
enabled: !!userId,
})
projects
쿼리는 다음과 같이 시작됩니다:
status: 'pending'
isPending: true
fetchStatus: 'idle'
사용자 정보가 이용 가능해지면 프로젝트 쿼리가 활성화되고 다음 상태로 전환됩니다:
status: 'pending'
isPending: true
fetchStatus: 'fetching'
프로젝트 정보를 가져오면 다음과 같이 됩니다:
status: 'success'
isPending: false
fetchStatus: 'idle'
동적 병렬 쿼리 - useQueries
도 이전 쿼리에 의존할 수 있습니다. 다음은 그 방법입니다:
// Get the users ids
const { data: userIds } = useQuery({
queryKey: ['users'],
queryFn: getUsersData,
select: (users) => users.map((user) => user.id),
})
// Then get the users messages
const usersMessages = useQueries({
queries: userIds
? userIds.map((id) => {
return {
queryKey: ['messages', id],
queryFn: () => getMessagesByUsers(id),
}
})
: [], // if users is undefined, an empty array will be returned
})
useQueries
는 쿼리 결과의 배열을 반환합니다.
종속 쿼리는 본질적으로 요청 워터폴을 구성하여 성능을 저하시킵니다. 두 쿼리 모두 동일한 시간이 걸리는 것처럼 가정하면 병렬이 아닌 직렬로 수행하는 것은 항상 두 배의 시간이 소요되며, 항상 두 배의 시간이 소요됩니다. 이는 특히 높은 지연 시간을 가진 클라이언트에서 문제가 됩니다. 가능하다면 백엔드 API를 재구성하여 두 쿼리를 병렬로 가져올 수 있도록 하는 것이 좋지만, 항상 실질적으로 가능하지는 않을 수 있습니다.
위 예제에서는 getUserByEmail
을 먼저 가져와서 getProjectsByUser
를 실행하는 대신, 새로운 getProjectsByUserEmail
쿼리를 도입하여 워터폴을 평탄화할 수 있습니다.
쿼리의 status === 'pending'
상태는 초기 로딩 상태를 표시하는 데 충분하지만, 때로는 쿼리가 백그라운드에서 다시 페칭되고 있음을 나타내는 추가 인디케이터를 표시하고 싶을 때가 있습니다. 이를 위해 쿼리에는 isFetching
boolean이 제공되며, 이를 사용하여 상태 변수의 상태와 관계없이 페칭 상태임을 표시할 수 있습니다.
function Todos() {
const {
status,
data: todos,
error,
isFetching,
} = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
})
return status === 'pending' ? (
<span>Loading...</span>
) : status === 'error' ? (
<span>Error: {error.message}</span>
) : (
<>
{isFetching ? <div>Refreshing...</div> : null}
<div>
{todos.map((todo) => (
<Todo todo={todo} />
))}
</div>
</>
)
}
개별 쿼리 로딩 상태 외에도, 모든 쿼리가 fetch 중일 때(백그라운드 페칭 포함) 전역 로딩 인디케이터를 표시하고 싶다면 useIsFetching
훅을 사용할 수 있습니다.
import { useIsFetching } from '@tanstack/react-query'
function GlobalLoadingIndicator() {
const isFetching = useIsFetching()
return isFetching ? (
<div>Queries are fetching in the background...</div>
) : null
}
사용자가 애플리케이션을 떠났다가 돌아올 때 쿼리 데이터가 오래된 경우, TanStack Query는 자동으로 백그라운드에서 새로운 데이터를 요청합니다. 이 기능은 전역적으로 또는 쿼리별로 refetchOnWindowFocus
옵션을 사용하여 비활성화할 수 있습니다.
//
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false, // default: true
},
},
})
function App() {
return <QueryClientProvider client={queryClient}>...</QueryClientProvider>
}
useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
refetchOnWindowFocus: false,
})
특정 상황에서 TanStack Query의 재검증을 트리거하는 윈도우 포커스 이벤트를 직접 관리하고 싶을 수 있습니다. 이를 위해 focusManager.setEventListener
함수를 사용하여 윈도우가 포커스될 때 실행되어야 하는 콜백을 설정할 수 있습니다. focusManager.setEventListener
를 호출하면 이전에 설정된 핸들러가 제거되고(대부분의 경우 기본 핸들러) 새로운 핸들러가 사용됩니다. 예를 들어, 기본 핸들러는 다음과 같습니다:
focusManager.setEventListener((handleFocus) => {
// Listen to visibilitychange
if (typeof window !== 'undefined' && window.addEventListener) {
const visibilitychangeHandler = () => {
handleFocus(document.visibilityState === 'visible')
}
window.addEventListener('visibilitychange', visibilitychangeHandler, false)
return () => {
// Be sure to unsubscribe if a new handler is set
window.removeEventListener('visibilitychange', visibilitychangeHandler)
}
}
})
React Native에서는 윈도우의 이벤트 리스너 대신 AppState 모듈을 통해 포커스 정보를 제공합니다. 앱 상태가 "active"로 변경될 때 업데이트를 트리거하려면 AppState의 "change" 이벤트를 사용할 수 있습니다:
import { AppState } from 'react-native'
import { focusManager } from '@tanstack/react-query'
function onAppStateChange(status: AppStateStatus) {
if (Platform.OS !== 'web') {
focusManager.setFocused(status === 'active')
}
}
useEffect(() => {
const subscription = AppState.addEventListener('change', onAppStateChange)
return () => subscription.remove()
}, [])
import { focusManager } from '@tanstack/react-query'
// Override the default focus state
focusManager.setFocused(true)
// Fallback to the default focus check
focusManager.setFocused(undefined)
쿼리를 자동 실행하지 않도록 설정하려면 enabled = false
옵션을 사용할 수 있습니다. 이 옵션은 boolean을 반환하는 콜백도 받을 수 있습니다.
enabled
가 false
일 때:
status === 'success'
또는 isSuccess
상태로 초기화됩니다.status === 'pending'
및 fetchStatus === 'idle'
상태로 시작됩니다.invalidateQueries
및 refetchQueries
호출을 무시합니다.useQuery
에서 반환된 refetch
를 사용하여 쿼리를 수동으로 페칭할 수 있습니다. 그러나 이는 skipToken
과 함께 사용할 수 없습니다.타입스크립트 사용자들은
enabled = false
대신skipToken
을 사용하는 것을 선호할 수 있습니다.
function Todos() {
const { isLoading, isError, data, error, refetch, isFetching } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodoList,
enabled: false,
})
return (
<div>
<button onClick={() => refetch()}>Fetch Todos</button>
{data ? (
<>
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</>
) : isError ? (
<span>Error: {error.message}</span>
) : isLoading ? (
<span>Loading...</span>
) : (
<span>Not ready ...</span>
)}
<div>{isFetching ? 'Fetching...' : null}</div>
</div>
)
}
쿼리를 영구적으로 비활성화하면 TanStack Query의 여러 훌륭한 기능(예: 백그라운드 재페칭)을 사용할 수 없게 되며, 이는 선언적 접근 방식(쿼리가 실행될 시점을 정의)에서 명령적 모드(여기 클릭할 때마다 페칭)로 전환하게 됩니다. 또한 refetch
에 매개변수를 전달하는 것도 불가능합니다. 종종 원하는 것은 초기 페칭을 미루는 지연 쿼리일 것입니다.
enabled
옵션을 사용하여 쿼리를 영구적으로 비활성화할 수 있을 뿐만 아니라 나중에 활성화/비활성화할 수도 있습니다. 예를 들어 필터 폼에서 사용자가 필터 값을 입력한 후에만 첫 번째 요청을 보내고 싶을 때 유용합니다:
function Todos() {
const [filter, setFilter] = React.useState('')
const { data } = useQuery({
queryKey: ['todos', filter],
queryFn: () => fetchTodos(filter),
// ⬇️ disabled as long as the filter is empty
enabled: !!filter,
})
return (
<div>
// 🚀 applying the filter will enable and execute the query
<FiltersForm onApply={setFilter} />
{data && <TodosTable data={data} />}
</div>
)
}
지연 쿼리는 시작부터 status: 'pending'
상태에 있으며, 이는 데이터가 아직 없다는 것을 의미합니다. 그러나 현재 데이터를 패칭하지 않고 있기 때문에(쿼리가 활성화되지 않음) 로딩 스피너를 표시하기 위해 이 플래그를 사용할 수 없습니다.
비활성화된 쿼리나 지연 쿼리를 사용하는 경우 isLoading
플래그를 사용할 수 있습니다. 이 플래그는 다음에서 파생된 플래그입니다:
isPending && isFetching
따라서 쿼리가 처음으로 데이터를 페칭하는 경우에만 true가 됩니다.
타입스크립트를 사용하는 경우, skipToken
을 사용하여 쿼리를 비활성화할 수 있습니다. 이는 조건에 따라 쿼리를 비활성화하고 싶지만 타입 안전을 유지하고 싶은 경우에 유용합니다.
중요:
useQuery
의refetch
는skipToken
과 함께 작동하지 않습니다. 그 외에는skipToken
이enabled: false
와 동일하게 작동합니다.
function Todos() {
const [filter, setFilter] = React.useState<string | undefined>()
const { data } = useQuery({
queryKey: ['todos', filter],
// ⬇️ disabled as long as the filter is undefined or empty
queryFn: filter ? () => fetchTodos(filter) : skipToken,
})
return (
<div>
// 🚀 applying the filter will enable and execute the query
<FiltersForm onApply={setFilter} />
{data && <TodosTable data={data} />}
</div>
)
}