new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

TanStack Query · React · all subjects

queries

310 notes in this subject, read out of this brain and free to use. This is page 3 of 6.

Query result states: isPending, isError, isSuccess

The result object from useQuery contains state indicators. A query can only be in one of the following states at any given moment: isPending (or status === 'pending') means the query has no data yet; isError (or status === 'error') means the query encountered an error; isSuccess (or status === 'success') means the query was successful and data is available.

Query result additional properties

Beyond the primary states, the query result provides additional information: error property is available when the query is in an isError state; data property is available when the query is in an isSuccess state; isFetching is true in any state if the query is fetching at any time, including during background refetching.

Recommended pattern for checking query states

For most queries, it is usually sufficient to check for the isPending state first, then the isError state, then finally assume that the data is available and render the successful state. TypeScript will also narrow the type of data correctly if you've checked for pending and error before accessing it.

fetchStatus property values

The query result includes a fetchStatus property with the following options: fetchStatus === 'fetching' means the query is currently fetching; fetchStatus === 'paused' means the query wanted to fetch but it is paused (related to Network Mode); fetchStatus === 'idle' means the query is not doing anything at the moment.

Difference between status and fetchStatus

The status gives information about the data: do we have any or not? The fetchStatus gives information about the queryFn: is it running or not? Background refetches and stale-while-revalidate logic make all combinations for status and fetchStatus possible. For example, a query in success status will usually be in idle fetchStatus, but it could also be in fetching if a background refetch is happening. A query that mounts with no data will usually be in pending status and fetching fetchStatus, but could also be paused if there is no network connection. A query can be in pending state without actually fetching data.

useQuery basic example

Example showing basic useQuery usage: ```tsx import { useQuery } from '@tanstack/react-query' function App() { const info = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList }) } ``` This shows the minimum required parameters: queryKey as a unique identifier and queryFn as a function that returns a promise.

useQuery with status string checks example

Example showing how to use status string to handle query states: ```tsx 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> ) } ```

When to use mutations instead of queries

If your method modifies data on the server, use Mutations instead of queries.

Prefetch in component example with useQuery

Example of prefetching in a parent component to flatten request waterfalls: ```tsx function Article({ id }) { const { data: articleData, isPending } = useQuery({ queryKey: ['article', id], queryFn: getArticleById, }) // Prefetch useQuery({ queryKey: ['article-comments', id], queryFn: getArticleCommentsById, notifyOnChangeProps: [], }) if (isPending) { return 'Loading article...' } return ( <> <ArticleHeader articleData={articleData} /> <ArticleBody articleData={articleData} /> <Comments id={id} /> </> ) } ```

prefetchQuery basic usage

prefetchQuery populates the cache with data ahead of time. Use it by calling queryClient.prefetchQuery() with queryKey and queryFn parameters. The results are cached like a normal query.

prefetchQuery uses default staleTime from queryClient

By default, prefetchQuery uses the default staleTime configured for the queryClient to determine whether existing cache data is fresh. You can override this by passing a specific staleTime value to the prefetchQuery call, but this only applies to the prefetch itself, not to subsequent useQuery calls for the same key.

ensureQueryData ignores staleTime

The ensureQueryData function can be used instead of prefetchQuery if you want to ignore staleTime and always return data if it's available in the cache.

prefetchQuery with higher staleTime for server-side prefetching

When prefetching on the server, it's recommended to set a default staleTime higher than 0 for that queryClient to avoid having to pass a specific staleTime to each prefetch call.

prefetchQuery returns Promise<void>

prefetchQuery and prefetchInfiniteQuery return Promise<void> and thus never return query data. If you need query data returned, use fetchQuery or fetchInfiniteQuery instead.

prefetchQuery does not throw errors

The prefetch functions never throw errors because they usually try to fetch again in a useQuery which provides a graceful fallback. If you need to catch errors, use fetchQuery or fetchInfiniteQuery instead.

Prefetch in event handlers

A straightforward form of prefetching is doing it when the user interacts with something. Use queryClient.prefetchQuery in onMouseEnter or onFocus event handlers. Set staleTime to ensure prefetch fires when data is older than the specified time.

Prefetch in event handlers example

Example of prefetching in event handlers: ```tsx function ShowDetailsButton() { const queryClient = useQueryClient() const prefetch = () => { queryClient.prefetchQuery({ queryKey: ['details'], queryFn: getDetailsData, staleTime: 60000, }) } return ( <button onMouseEnter={prefetch} onFocus={prefetch} onClick={...}> Show Details </button> ) } ```

Prefetch in components to avoid request waterfall

Use prefetching during component lifecycle when you know a child or descendant will need data but can't render until another query finishes. This can be done using a useQuery call and ignoring the result, with notifyOnChangeProps: [] as an optional optimization to avoid rerenders.

usePrefetchQuery hook for prefetching with Suspense

When prefetching together with Suspense, you cannot use useSuspenseQueries to prefetch (it would block rendering) or useQuery for prefetch (it wouldn't start until after suspenseful query resolved). Instead, use usePrefetchQuery or usePrefetchInfiniteQuery hooks.

Prefetch with Suspense using usePrefetchQuery

Example of prefetching with Suspense using usePrefetchQuery hook: ```tsx function ArticleLayout({ id }) { usePrefetchQuery({ queryKey: ['article-comments', id], queryFn: getArticleCommentsById, }) return ( <Suspense fallback="Loading article"> <Article id={id} /> </Suspense> ) } function Article({ id }) { const { data: articleData, isPending } = useSuspenseQuery({ queryKey: ['article', id], queryFn: getArticleById, }) ... } ```

Prefetch inside query function

Prefetch can be done inside the query function using queryClient.prefetchQuery(). This makes sense when you know that every time a query is fetched, another query will also likely be needed.

Prefetch inside query function example

Example of prefetching inside a query function: ```tsx const queryClient = useQueryClient() const { data: articleData, isPending } = useQuery({ queryKey: ['article', id], queryFn: (...args) => { queryClient.prefetchQuery({ queryKey: ['article-comments', id], queryFn: getArticleCommentsById, }) return getArticleById(...args) }, }) ```

Prefetch in effect

Prefetching in an effect works, but note that if you are using useSuspenseQuery in the same component, the effect won't run until after the query finishes, which might not be what you want.

Prefetch in effect example

Example of prefetching in a useEffect: ```tsx const queryClient = useQueryClient() useEffect(() => { queryClient.prefetchQuery({ queryKey: ['article-comments', id], queryFn: getArticleCommentsById, }) }, [queryClient, id]) ```

Conditional prefetching based on other query results

Prefetch conditionally based on the result of another fetch by doing prefetching inside the query function. This allows you to inspect the fetched data and decide what else needs to be prefetched.

Conditional prefetching example with code splitting

Example of conditional prefetching based on feed item type: ```tsx function Feed() { const queryClient = useQueryClient() const { data, isPending } = useQuery({ queryKey: ['feed'], queryFn: async (...args) => { const feed = await getFeed(...args) for (const feedItem of feed) { if (feedItem.type === 'GRAPH') { queryClient.prefetchQuery({ queryKey: ['graph', feedItem.id], queryFn: getGraphDataById, }) } } return feed } }) ... } ```

Router integration for prefetching

Integrate prefetching at the router level to avoid request waterfalls. Explicitly declare for each route what data is needed, ahead of time. This allows you to either block rendering until all data is present, or start a prefetch without awaiting the result, or mix both approaches.

TanStack Router integration example with prefetching

Example of integrating TanStack Router with prefetching: ```tsx const queryClient = new QueryClient() const routerContext = new RouterContext() const rootRoute = routerContext.createRootRoute({ component: () => { ... } }) const articleRoute = new Route({ getParentRoute: () => rootRoute, path: 'article', beforeLoad: () => { return { articleQueryOptions: { queryKey: ['article'], queryFn: fetchArticle }, commentsQueryOptions: { queryKey: ['comments'], queryFn: fetchComments }, } }, loader: async ({ context: { queryClient }, routeContext: { articleQueryOptions, commentsQueryOptions }, }) => { // Fetch comments asap, but don't block queryClient.prefetchQuery(commentsQueryOptions) // Don't render the route at all until article has been fetched await queryClient.prefetchQuery(articleQueryOptions) }, component: ({ useRouteContext }) => { const { articleQueryOptions, commentsQueryOptions } = useRouteContext() const articleQuery = useQuery(articleQueryOptions) const commentsQuery = useQuery(commentsQueryOptions) return ( ... ) }, errorComponent: () => 'Oh crap!', }) ```

Manually prime a query with setQueryData

If you already have data for a query synchronously available, use the Query Client's setQueryData method to directly add or update a query's cached result by key instead of prefetching.

setQueryData example

Example of manually priming a query with setQueryData: ```tsx queryClient.setQueryData(['todos'], todos) ```

Prefetching patterns overview

There are four main prefetching patterns: in event handlers, in components, via router integration, and during server rendering. Server rendering is covered separately in the Server Rendering & Hydration guide.

Prefetching avoids request waterfalls

One specific use of prefetching is to avoid Request Waterfalls. See the Performance & Request Waterfalls guide for in-depth background and explanation.

prefetchQuery garbage collection

If no instances of useQuery appear for a prefetched query, it will be deleted and garbage collected after the time specified in gcTime.

Using AbortSignal with axios versions before v0.22.0

For axios versions lower than v0.22.0, create a CancelToken source and pass its token to the request. Listen to the abort event on the signal and call source.cancel() when abort is triggered.

Example: axios v0.22.0+ with query cancellation

import axios from 'axios' const query = useQuery({ queryKey: ['todos'], queryFn: ({ signal }) => axios.get('/todos', { // Pass the signal to `axios` signal, }), })

Example: cancel options with silent suppression

await queryClient.cancelQueries({ queryKey: ['posts'] }, { silent: true })

Query cancellation does not work with Suspense hooks

Cancellation does not work when working with Suspense hooks: useSuspenseQuery, useSuspenseQueries, and useSuspenseInfiniteQuery.

Cancel options for queryClient.cancelQueries

Cancel options control the behavior of query cancellation operations. The silent option (boolean, defaults to false) suppresses propagation of CancelledError to observers and related notifications when true. The revert option (boolean, defaults to true) restores the query's state from immediately before the in-flight fetch, sets fetchStatus back to idle, and only throws if there was no prior data when true.

Example: manual query cancellation with cancel button

const query = useQuery({ queryKey: ['todos'], queryFn: async ({ signal }) => { const resp = await fetch('/todos', { signal }) return resp.json() }, }) const queryClient = useQueryClient() return ( <button onClick={(e) => { e.preventDefault() queryClient.cancelQueries({ queryKey: ['todos'] }) }} > Cancel </button> )

Manual query cancellation with queryClient.cancelQueries

To manually cancel a query, call queryClient.cancelQueries({ queryKey }). This will cancel the query and revert it back to its previous state. If you have consumed the signal passed to the query function, TanStack Query will additionally also cancel the Promise.

Example: XMLHttpRequest with query cancellation

const query = useQuery({ queryKey: ['todos'], queryFn: ({ signal }) => { return new Promise((resolve, reject) => { var oReq = new XMLHttpRequest() oReq.addEventListener('load', () => { resolve(JSON.parse(oReq.responseText)) }) signal?.addEventListener('abort', () => { oReq.abort() reject() }) oReq.open('GET', '/todos') oReq.send() }) }, })

Using AbortSignal with XMLHttpRequest

To use cancellation with XMLHttpRequest, create a Promise that handles the XMLHttpRequest. Listen to the abort event on the signal and call oReq.abort() when the abort event fires.

AbortController API runtime environment support

The AbortController API is available in most runtime environments. If your runtime environment does not support it, you will need to provide a polyfill. Several polyfill options are available on npm.

AbortSignal passed to query function for cancellation

TanStack Query provides each query function with an AbortSignal instance. When a query becomes out-of-date or inactive, this signal will become aborted. All queries are cancellable, and you can respond to the cancellation inside your query function.

Default behavior when query unmounts before promise resolves

By default, queries that unmount or become unused before their promises are resolved are not cancelled. After the promise resolves, the resulting data will be available in the cache. This is helpful if a component unmounts before a query finishes, and if you mount the component again and the query has not been garbage collected yet, data will be available. However, if you consume the AbortSignal, the Promise will be cancelled and the Query must be cancelled, reverting its state to its previous state.

Using AbortSignal with fetch API

To use cancellation with fetch, pass the signal parameter from the query function to the fetch call's signal option. You can pass it to one fetch call or several fetch calls.

Example: fetch with query cancellation

const query = useQuery({ queryKey: ['todos'], queryFn: async ({ signal }) => { const todosResponse = await fetch('/todos', { // Pass the signal to one fetch signal, }) const todos = await todosResponse.json() const todoDetails = todos.map(async ({ details }) => { const response = await fetch(details, { // Or pass it to several signal, }) return response.json() }) return Promise.all(todoDetails) }, })

Using AbortSignal with axios v0.22.0 or later

For axios v0.22.0 and later, pass the signal parameter from the query function directly to the signal option in the axios.get call.

Example: axios < v0.22.0 with query cancellation

import axios from 'axios' const query = useQuery({ queryKey: ['todos'], queryFn: ({ signal }) => { // Create a new CancelToken source for this request const CancelToken = axios.CancelToken const source = CancelToken.source() const promise = axios.get('/todos', { // Pass the source token to your request cancelToken: source.token, }) // Cancel the request if TanStack Query signals to abort signal?.addEventListener('abort', () => { source.cancel('Query was cancelled by TanStack Query') }) return promise }, })

Valid query function configurations

The following are valid query function configurations: ```tsx 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]), }) ```

Query function must return a promise

A query function can be any function that returns a promise. The promise should either resolve the data or throw an error.

Query resolved value cannot be undefined

On success, the resolved value may be anything except undefined. Queries that resolve to undefined will be treated as failed. To store "nothing" as a successful result in the query cache, resolve null instead.

Query error handling must throw or reject

For TanStack Query to determine a query has errored, the query function must throw or return a rejected Promise. Any error that is thrown in the query function will be persisted on the error state of the query.

Throwing errors in query functions

Errors can be thrown directly or by returning a rejected Promise. Example: ```tsx 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 }, }) ```

Fetch API error handling in query functions

Unlike axios or graphql-request, the fetch API does not throw errors by default for unsuccessful HTTP calls. You must manually check the response and throw errors. Example: ```tsx 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() }, }) ```

Query keys passed to query function via QueryFunctionContext

Query keys are passed into your query function as part of the QueryFunctionContext. This makes it possible to extract your query functions if needed. Example: ```tsx 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 properties

The QueryFunctionContext is the object passed to each query function. It consists of: - queryKey: QueryKey - the query keys used to identify the query - client: QueryClient - the QueryClient instance - signal?: AbortSignal - an AbortSignal instance provided by TanStack Query, can be used for Query Cancellation - meta: Record<string, unknown> | undefined - an optional field you can fill with additional information about your query Additionally, Infinite Queries get the following options passed: - pageParam: TPageParam - the page parameter used to fetch the current page - direction: 'forward' | 'backward' - deprecated, the direction of the current page fetch. To get access to the direction of the current page fetch, add a direction to pageParam from getNextPageParam and getPreviousPageParam.

queryOptions helper for sharing query configuration

The queryOptions helper allows you to define query configuration in one place, co-locating queryKey and queryFn. At runtime it returns whatever is passed into it. It provides advantages when using TypeScript, enabling type inference and type safety for all query options.

queryOptions with select override example

Example of overriding queryOptions with a select function: const query = useQuery({ ...groupOptions(1), select: (data) => data.groupName }); The data type is inferred from the select return type, not the original queryFn.

Override queryOptions at component level with select

You can override options from queryOptions at the component level using the select function. A common pattern is to create per-component select functions that transform query data. Type inference still works, so query.data will be the return type of select instead of queryFn.

Give your agent this brain