Hooks for managing, caching and syncing asynchronous and remote data in React
—
This document covers utility hooks for state inspection, prefetching, and restoration management in @tanstack/react-query.
Tracks the number of queries currently in fetching state, with optional filtering.
function useIsFetching(
filters?: QueryFilters,
queryClient?: QueryClient
): numberParameters:
filters (optional): QueryFilters - Filters to limit which queries to countqueryClient (optional): QueryClient - Custom QueryClient instanceReturns: number - Count of currently fetching queries
Example:
import { useIsFetching } from '@tanstack/react-query'
function LoadingIndicator() {
const fetchingCount = useIsFetching()
if (fetchingCount > 0) {
return <div>Loading {fetchingCount} queries...</div>
}
return null
}
// With filters
function UserQueriesLoadingIndicator() {
const userFetchingCount = useIsFetching({
queryKey: ['users'],
exact: false
})
return userFetchingCount > 0 ? <div>Loading user data...</div> : null
}Tracks the number of mutations currently in pending state, with optional filtering.
function useIsMutating(
filters?: MutationFilters,
queryClient?: QueryClient
): numberParameters:
filters (optional): MutationFilters - Filters to limit which mutations to countqueryClient (optional): QueryClient - Custom QueryClient instanceReturns: number - Count of currently pending mutations
Example:
import { useIsMutating } from '@tanstack/react-query'
function MutationLoadingIndicator() {
const mutatingCount = useIsMutating()
if (mutatingCount > 0) {
return <div>Saving changes...</div>
}
return null
}
// With filters for specific mutation
function UserUpdateLoadingIndicator() {
const isUpdatingUser = useIsMutating({
mutationKey: ['updateUser']
})
return isUpdatingUser > 0 ? <div>Updating user...</div> : null
}Subscribes to the mutation cache and returns selected mutation states.
function useMutationState<TResult = MutationState>(
options?: {
filters?: MutationFilters
select?: (mutation: Mutation) => TResult
},
queryClient?: QueryClient
): Array<TResult>Parameters:
options (optional): Configuration object
filters (optional): MutationFilters - Filters for mutation selectionselect (optional): (mutation: Mutation) => TResult - Transform function for each mutationqueryClient (optional): QueryClient - Custom QueryClient instanceReturns: Array<TResult> - Array of selected/transformed mutation states
Example:
import { useMutationState } from '@tanstack/react-query'
function PendingMutations() {
const pendingMutations = useMutationState({
filters: { status: 'pending' },
select: (mutation) => ({
mutationKey: mutation.options.mutationKey,
submittedAt: mutation.state.submittedAt,
})
})
return (
<div>
{pendingMutations.map((mutation, index) => (
<div key={index}>
Mutation {mutation.mutationKey?.join(' ')} pending since{' '}
{new Date(mutation.submittedAt!).toLocaleTimeString()}
</div>
))}
</div>
)
}
// Get all mutation variables for failed mutations
function FailedMutationData() {
const failedVariables = useMutationState({
filters: { status: 'error' },
select: (mutation) => mutation.state.variables,
})
return <div>Failed mutations: {failedVariables.length}</div>
}Prefetches a query during component render if it's not already in cache.
function usePrefetchQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey
>(
options: UsePrefetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
queryClient?: QueryClient
): voidParameters:
options: UsePrefetchQueryOptions<TQueryFnData, TError, TData, TQueryKey> - Query configuration for prefetchingqueryClient (optional): QueryClient - Custom QueryClient instanceReturns: void
Key Features:
Example:
import { usePrefetchQuery } from '@tanstack/react-query'
function UserProfilePage({ userId }: { userId: string }) {
// Prefetch user posts when viewing profile
usePrefetchQuery({
queryKey: ['users', userId, 'posts'],
queryFn: () => fetchUserPosts(userId),
staleTime: 5 * 60 * 1000, // 5 minutes
})
// Main profile query
const { data: user } = useQuery({
queryKey: ['users', userId],
queryFn: () => fetchUser(userId),
})
return (
<div>
<h1>{user?.name}</h1>
{/* Posts will be instantly available when navigating to posts tab */}
</div>
)
}
// Conditional prefetching
function ConditionalPrefetch({ shouldPrefetch, userId }: {
shouldPrefetch: boolean
userId: string
}) {
usePrefetchQuery({
queryKey: ['users', userId, 'settings'],
queryFn: () => fetchUserSettings(userId),
enabled: shouldPrefetch, // Only prefetch when needed
})
return <div>Component content</div>
}Prefetches an infinite query during component render if it's not already in cache.
function usePrefetchInfiniteQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown
>(
options: FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
queryClient?: QueryClient
): voidParameters:
options: FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> - Infinite query configurationqueryClient (optional): QueryClient - Custom QueryClient instanceReturns: void
Key Features:
Example:
import { usePrefetchInfiniteQuery } from '@tanstack/react-query'
function HomePage() {
// Prefetch first page of user posts for instant loading
usePrefetchInfiniteQuery({
queryKey: ['posts'],
queryFn: ({ pageParam = 0 }) => fetchPosts(pageParam),
getNextPageParam: (lastPage) => lastPage.nextCursor,
pages: 1, // Only prefetch first page
})
return (
<div>
<h1>Welcome</h1>
<Link to="/posts">View Posts</Link> {/* Will load instantly */}
</div>
)
}
// Prefetch with initial page param
function CategoryPage({ categoryId }: { categoryId: string }) {
usePrefetchInfiniteQuery({
queryKey: ['posts', { category: categoryId }],
queryFn: ({ pageParam = 0 }) => fetchPostsByCategory(categoryId, pageParam),
getNextPageParam: (lastPage) => lastPage.nextCursor,
initialPageParam: 0,
})
return <div>Category content</div>
}Indicates whether the app is currently in the hydration/restoration phase during SSR.
function useIsRestoring(): booleanReturns: boolean - true if currently restoring from server state, false otherwise
Key Features:
Example:
import { useIsRestoring } from '@tanstack/react-query'
function UserProfile() {
const isRestoring = useIsRestoring()
const { data: user, isPending } = useQuery({
queryKey: ['user'],
queryFn: fetchUser,
})
// Show different loading state during hydration
if (isRestoring) {
return <div>Restoring user data...</div>
}
if (isPending) {
return <div>Loading user...</div>
}
return <div>Welcome, {user.name}!</div>
}
// Conditional rendering during hydration
function ConditionalContent() {
const isRestoring = useIsRestoring()
// Don't render complex components during hydration
if (isRestoring) {
return <div>Loading app...</div>
}
return <ComplexInteractiveComponent />
}
// Custom hook for handling restoration state
function useHydrationSafeQuery(options: UseQueryOptions) {
const isRestoring = useIsRestoring()
return useQuery({
...options,
enabled: !isRestoring && (options.enabled ?? true),
})
}interface QueryFilters {
queryKey?: QueryKey
exact?: boolean
type?: 'active' | 'inactive' | 'all'
stale?: boolean
fetchStatus?: FetchStatus
predicate?: (query: Query) => boolean
}
interface MutationFilters {
mutationKey?: MutationKey
exact?: boolean
status?: MutationStatus
predicate?: (mutation: Mutation) => boolean
}
interface UsePrefetchQueryOptions<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey
> extends Omit<FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'queryFn'> {
queryFn?: Exclude<
FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>['queryFn'],
SkipToken
>
}The following utility functions are available from the core query system for advanced use cases:
Generates a deterministic hash string from a query key for internal use.
function hashKey(queryKey: QueryKey): stringParameters:
queryKey: QueryKey - The query key to hashReturns: string - Deterministic hash string
Example:
import { hashKey } from '@tanstack/react-query'
const key1 = ['users', { id: 1 }]
const key2 = ['users', { id: 1 }]
const hash1 = hashKey(key1)
const hash2 = hashKey(key2)
console.log(hash1 === hash2) // true - same content produces same hashChecks if a query matches the provided filters.
function matchQuery(
filters: QueryFilters,
query: Query
): booleanParameters:
filters: QueryFilters - The filters to match againstquery: Query - The query instance to checkReturns: boolean - Whether the query matches the filters
Checks if a mutation matches the provided filters.
function matchMutation(
filters: MutationFilters,
mutation: Mutation
): booleanParameters:
filters: MutationFilters - The filters to match againstmutation: Mutation - The mutation instance to checkReturns: boolean - Whether the mutation matches the filters
Utility function to keep previous data during refetches, useful for pagination.
function keepPreviousData<T>(
previousData: T | undefined,
previousQuery: Query | undefined
): T | undefinedParameters:
previousData: T | undefined - The previous query datapreviousQuery: Query | undefined - The previous query instanceReturns: T | undefined - The data to use during refetch
Example:
import { useQuery, keepPreviousData } from '@tanstack/react-query'
function PaginatedPosts({ page }: { page: number }) {
const { data, isPending, isPlaceholderData } = useQuery({
queryKey: ['posts', page],
queryFn: () => fetchPosts(page),
placeholderData: keepPreviousData,
})
return (
<div>
{data?.posts.map(post => <div key={post.id}>{post.title}</div>)}
{isPending && !isPlaceholderData && <div>Loading...</div>}
{isPlaceholderData && <div>Loading new page...</div>}
</div>
)
}Special token that can be passed to queryFn to skip query execution.
const skipToken: unique symbolExample:
import { useQuery, skipToken } from '@tanstack/react-query'
function ConditionalQuery({ userId, enabled }: {
userId: string | null
enabled: boolean
}) {
const { data } = useQuery({
queryKey: ['user', userId],
queryFn: enabled && userId ? () => fetchUser(userId) : skipToken,
})
return <div>{data?.name}</div>
}Error class for cancelled query operations.
class CancelledError extends Error {
constructor(options?: { revert?: boolean })
}Utility to check if an error is a cancellation error.
function isCancelledError(value: any): value is CancelledErrorExample:
import { useMutation, isCancelledError } from '@tanstack/react-query'
function UpdateUser() {
const mutation = useMutation({
mutationFn: updateUser,
onError: (error) => {
if (isCancelledError(error)) {
console.log('Update was cancelled')
} else {
console.log('Update failed:', error.message)
}
}
})
return (
<button onClick={() => mutation.mutate(userData)}>
Update User
</button>
)
}Experimental streaming query functionality for advanced use cases
const experimental_streamedQuery: unique symbol⚠️ Warning: This is an experimental API that may change or be removed in future versions. Use with caution in production environments.
This experimental feature is designed for advanced streaming data scenarios and is subject to breaking changes. Refer to the official TanStack Query documentation for the latest information about experimental features.
All utility hooks are designed to be safe and non-throwing:
Common Error Scenarios:
Install with Tessl CLI
npx tessl i tessl/npm-tanstack--react-query