Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/quiet-flags-remove.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@tanstack/angular-query-experimental': patch
'@tanstack/lit-query': patch
'@tanstack/preact-query': patch
'@tanstack/query-core': patch
'@tanstack/react-query': patch
'@tanstack/solid-query': patch
'@tanstack/svelte-query': patch
'@tanstack/vue-query': patch
---

Remove experimental render-time prefetching and the `promise` property from query results.
54 changes: 0 additions & 54 deletions docs/framework/react/guides/suspense.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ React Query can also be used with React's Suspense for Data Fetching APIs. For t
- [useSuspenseQuery](../reference/useSuspenseQuery.md)
- [useSuspenseInfiniteQuery](../reference/useSuspenseInfiniteQuery.md)
- [useSuspenseQueries](../reference/useSuspenseQueries.md)
- Additionally, you can use the `useQuery().promise` and `React.use()` (Experimental)

When using suspense mode, `status` states and `error` objects are not needed and are then replaced by usage of the `React.Suspense` component (including the use of the `fallback` prop and React error boundaries for catching errors). Please read the [Resetting Error Boundaries](#resetting-error-boundaries) and look at the [Suspense Example](../examples/suspense) for more information on how to set up suspense mode.

Expand Down Expand Up @@ -173,56 +172,3 @@ export function Providers(props: { children: React.ReactNode }) {
```

For more information, check out the [NextJs Suspense Streaming Example](../examples/nextjs-suspense-streaming) and the [Advanced Rendering & Hydration](./advanced-ssr.md) guide.

## Using `useQuery().promise` and `React.use()` (Experimental)

> To enable this feature, you need to set the `experimental_prefetchInRender` option to `true` when creating your `QueryClient`

**Example code:**

```tsx
const queryClient = new QueryClient({
defaultOptions: {
queries: {
experimental_prefetchInRender: true,
},
},
})
```

**Usage:**

```tsx
import React from 'react'
import { useQuery } from '@tanstack/react-query'
import { fetchTodos, type Todo } from './api'

function TodoList({ query }: { query: UseQueryResult<Todo[]> }) {
const data = React.use(query.promise)

return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}

export function App() {
const query = useQuery({ queryKey: ['todos'], queryFn: fetchTodos })

return (
<>
<h1>Todos</h1>
<React.Suspense fallback={<div>Loading...</div>}>
<TodoList query={query} />
</React.Suspense>
</>
)
}
```

For a more complete example, see [suspense example on GitHub](https://github.com/TanStack/query/tree/main/examples/react/suspense).

For a Next.js streaming example, see [nextjs-suspense-streaming example on GitHub](https://github.com/TanStack/query/tree/main/examples/react/nextjs-suspense-streaming).
6 changes: 0 additions & 6 deletions docs/framework/react/reference/queryOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ You can generally pass everything to `queryOptions` that you can also pass to [`
- `queryKey: QueryKey`
- **Required**
- The query key to generate options for.
- `experimental_prefetchInRender?: boolean`
- Optional
- Defaults to `false`
- When set to `true`, queries will be prefetched during render, which can be useful for certain optimization scenarios
- Needs to be turned on for the experimental `useQuery().promise` functionality

[//]: # 'Materials'

## Further reading
Expand Down
8 changes: 0 additions & 8 deletions docs/framework/react/reference/useInfiniteQuery.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const {
hasPreviousPage,
isFetchingNextPage,
isFetchingPreviousPage,
promise,
...result
} = useInfiniteQuery({
queryKey,
Expand Down Expand Up @@ -86,11 +85,4 @@ The returned properties for `useInfiniteQuery` are identical to the [`useQuery`
- Is the same as `isFetching && !isPending && !isFetchingNextPage && !isFetchingPreviousPage`
- `isRefetchError: boolean`
- Will be `true` if the query failed while refetching a page.
- `promise: Promise<TData>`
- A stable promise that resolves to the query result.
[//]: # 'ReactUse'
- This can be used with `React.use()` to fetch data
[//]: # 'ReactUse'
- Requires the `experimental_prefetchInRender` feature flag to be enabled on the `QueryClient`.

Keep in mind that imperative fetch calls, such as `fetchNextPage`, may interfere with the default refetch behaviour, resulting in outdated data. Make sure to call these functions only in response to user actions, or add conditions like `hasNextPage && !isFetching`.
4 changes: 0 additions & 4 deletions docs/framework/react/reference/useQuery.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ const {
isStale,
isSuccess,
isEnabled,
promise,
refetch,
status,
} = useQuery(
Expand Down Expand Up @@ -257,6 +256,3 @@ const {
- Defaults to `true`
- Per default, a currently running request will be cancelled before a new request is made
- When set to `false`, no refetch will be made if there is already a request running.
- `promise: Promise<TData>`
- A stable promise that will be resolved with the data of the query.
- Requires the `experimental_prefetchInRender` feature flag to be enabled on the `QueryClient`.
1 change: 0 additions & 1 deletion packages/lit-query/src/createInfiniteQueryController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ function createPendingInfiniteQueryResult<
isFetchingNextPage: false,
isFetchPreviousPageError: false,
isFetchingPreviousPage: false,
promise: Promise.resolve(undefined as never),
} as unknown as InfiniteQueryObserverResult<TData, TError>
}

Expand Down
2 changes: 0 additions & 2 deletions packages/lit-query/src/createQueriesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,6 @@ function createPendingQueryObserverResult(): QueryObserverResult {
Promise.reject(
createMissingQueryClientError(),
)) as QueryObserverResult['refetch'],
promise: Promise.resolve(undefined as never),
} as unknown as QueryObserverResult
}

Expand Down Expand Up @@ -282,7 +281,6 @@ function createPlaceholderQueryObserverResult(
isLoading: false,
isSuccess: true,
status: 'success',
promise: Promise.resolve(data as never),
} as QueryObserverResult
}

Expand Down
1 change: 0 additions & 1 deletion packages/lit-query/src/createQueryController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ function createPendingQueryResult<TData, TError>(): QueryObserverResult<
TData,
TError
>['refetch'],
promise: Promise.resolve(undefined as never),
} as unknown as QueryObserverResult<TData, TError>
}

Expand Down
7 changes: 0 additions & 7 deletions packages/preact-query/src/__tests__/useInfiniteQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,6 @@ describe('useInfiniteQuery', () => {
queryCache = new QueryCache()
queryClient = new QueryClient({
queryCache,
defaultOptions: {
queries: {
experimental_prefetchInRender: true,
},
},
})
})

Expand Down Expand Up @@ -105,7 +100,6 @@ describe('useInfiniteQuery', () => {
refetch: expect.any(Function),
status: 'pending',
fetchStatus: 'fetching',
promise: expect.any(Promise),
})
expect(states[1]).toEqual({
data: { pages: [0], pageParams: [0] },
Expand Down Expand Up @@ -141,7 +135,6 @@ describe('useInfiniteQuery', () => {
refetch: expect.any(Function),
status: 'success',
fetchStatus: 'idle',
promise: expect.any(Promise),
})
})

Expand Down
2 changes: 1 addition & 1 deletion packages/preact-query/src/__tests__/useQuery.test-d.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('useQuery', () => {
const fromQueryFn = useQuery({ queryKey: key, queryFn: () => 'test' })
expectTypeOf(fromQueryFn.data).toEqualTypeOf<string | undefined>()
expectTypeOf(fromQueryFn.error).toEqualTypeOf<Error | null>()
expectTypeOf(fromQueryFn.promise).toEqualTypeOf<Promise<string>>()
expectTypeOf(fromQueryFn).not.toHaveProperty('promise')

// it should be possible to specify the result type
const withResult = useQuery<string>({
Expand Down
10 changes: 0 additions & 10 deletions packages/preact-query/src/__tests__/useQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ describe('useQuery', () => {
refetch: expect.any(Function),
status: 'pending',
fetchStatus: 'fetching',
promise: expect.any(Promise),
})

expect(states[1]).toEqual({
Expand Down Expand Up @@ -161,10 +160,7 @@ describe('useQuery', () => {
refetch: expect.any(Function),
status: 'success',
fetchStatus: 'idle',
promise: expect.any(Promise),
})

expect(states[0]!.promise).toEqual(states[1]!.promise)
})

it('should return the correct states for an unsuccessful query', async () => {
Expand Down Expand Up @@ -224,7 +220,6 @@ describe('useQuery', () => {
refetch: expect.any(Function),
status: 'pending',
fetchStatus: 'fetching',
promise: expect.any(Promise),
})

expect(states[1]).toEqual({
Expand Down Expand Up @@ -253,7 +248,6 @@ describe('useQuery', () => {
refetch: expect.any(Function),
status: 'pending',
fetchStatus: 'fetching',
promise: expect.any(Promise),
})

expect(states[2]).toEqual({
Expand Down Expand Up @@ -282,11 +276,7 @@ describe('useQuery', () => {
refetch: expect.any(Function),
status: 'error',
fetchStatus: 'idle',
promise: expect.any(Promise),
})

expect(states[0]!.promise).toEqual(states[1]!.promise)
expect(states[1]!.promise).toEqual(states[2]!.promise)
})

it('should set isFetchedAfterMount to true after a query has been fetched', async () => {
Expand Down
6 changes: 1 addition & 5 deletions packages/preact-query/src/errorBoundaryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ export const ensurePreventErrorBoundaryRetry = <
>,
errorResetBoundary: QueryErrorResetBoundaryValue,
) => {
if (
options.suspense ||
options.throwOnError ||
options.experimental_prefetchInRender
) {
if (options.suspense || options.throwOnError) {
// Prevent retrying failed query if the error boundary has not been reset yet
if (!errorResetBoundary.isReset()) {
options.retryOnMount = false
Expand Down
5 changes: 0 additions & 5 deletions packages/preact-query/src/suspense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@ export const ensureSuspenseTimers = (
}
}

export const willFetch = (
result: QueryObserverResult<any, any>,
isRestoring: boolean,
) => result.isLoading && result.isFetching && !isRestoring

export const shouldSuspend = (
defaultedOptions:
| DefaultedQueryObserverOptions<any, any, any, any, any>
Expand Down
4 changes: 2 additions & 2 deletions packages/preact-query/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export type UseSuspenseQueryResult<
TError = DefaultError,
> = DistributiveOmit<
DefinedQueryObserverResult<TData, TError>,
'isPlaceholderData' | 'promise'
'isPlaceholderData'
>

export type DefinedUseQueryResult<
Expand All @@ -185,7 +185,7 @@ export type UseSuspenseInfiniteQueryResult<
TError = DefaultError,
> = OmitKeyof<
DefinedInfiniteQueryObserverResult<TData, TError>,
'isPlaceholderData' | 'promise'
'isPlaceholderData'
>

export type AnyUseMutationOptions = UseMutationOptions<any, any, any, any>
Expand Down
25 changes: 1 addition & 24 deletions packages/preact-query/src/useBaseQuery.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { environmentManager, noop, notifyManager } from '@tanstack/query-core'
import { noop, notifyManager } from '@tanstack/query-core'
import type {
QueryClient,
QueryKey,
Expand All @@ -19,7 +19,6 @@ import {
ensureSuspenseTimers,
fetchOptimistic,
shouldSuspend,
willFetch,
} from './suspense'
import type { UseBaseQueryOptions } from './types'
import { useSyncExternalStore } from './utils'
Expand Down Expand Up @@ -76,11 +75,6 @@ export function useBaseQuery<

useClearResetErrorBoundary(errorResetBoundary)

// this needs to be invoked before creating the Observer because that can create a cache entry
const isNewCacheEntry = !client
.getQueryCache()
.get(defaultedOptions.queryHash)

const [observer] = useState(
() =>
new Observer<TQueryFnData, TError, TData, TQueryData, TQueryKey>(
Expand Down Expand Up @@ -145,23 +139,6 @@ export function useBaseQuery<
result,
)

if (
defaultedOptions.experimental_prefetchInRender &&
!environmentManager.isServer() &&
willFetch(result, isRestoring)
) {
const promise = isNewCacheEntry
? // Fetch immediately on render in order to ensure `.promise` is resolved even if the component is unmounted
fetchOptimistic(defaultedOptions, observer, errorResetBoundary)
: // subscribe to the "cache promise" so that we can finalize the currentThenable once data comes in
client.getQueryCache().get(defaultedOptions.queryHash)?.promise

promise?.catch(noop).finally(() => {
// `.updateResult()` will trigger `.#currentThenable` to finalize
observer.updateResult()
})
}

// Handle result property usage tracking
return !defaultedOptions.notifyOnChangeProps
? observer.trackResult(result)
Expand Down
Loading
Loading