From 12b53a47cba35bdd1392fd493b3046a481d13b44 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Tue, 18 Jul 2023 17:20:48 +0200 Subject: [PATCH 01/12] feat: useSuspenseQuery --- packages/react-query/src/useQuery.ts | 2 -- packages/react-query/src/useSuspenseQuery.ts | 25 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 packages/react-query/src/useSuspenseQuery.ts diff --git a/packages/react-query/src/useQuery.ts b/packages/react-query/src/useQuery.ts index b25d049c735..0a673b9a6f7 100644 --- a/packages/react-query/src/useQuery.ts +++ b/packages/react-query/src/useQuery.ts @@ -12,8 +12,6 @@ import type { UndefinedInitialDataOptions, } from './queryOptions' -// HOOK - export function useQuery< TQueryFnData = unknown, TError = DefaultError, diff --git a/packages/react-query/src/useSuspenseQuery.ts b/packages/react-query/src/useSuspenseQuery.ts new file mode 100644 index 00000000000..38951d1cd45 --- /dev/null +++ b/packages/react-query/src/useSuspenseQuery.ts @@ -0,0 +1,25 @@ +'use client' +import { QueryObserver } from '@tanstack/query-core' +import { useBaseQuery } from './useBaseQuery' +import type { UseQueryOptions } from './types' +import type { DefaultError, QueryClient, QueryKey } from '@tanstack/query-core' +import type { DefinedUseQueryResult } from './types' + +export function useSuspenseQuery< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +>( + options: Omit< + UseQueryOptions, + 'enabled' | 'suspense' | 'throwOnError' | 'placeholderData' + >, + queryClient?: QueryClient, +): DefinedUseQueryResult { + return useBaseQuery( + options, + QueryObserver, + queryClient, + ) as DefinedUseQueryResult +} From bd93c67b002bf95979dc5763b8e50d59d6cdb3a8 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Tue, 18 Jul 2023 17:21:19 +0200 Subject: [PATCH 02/12] feat: infiniteQueryOptions --- .../react-query/src/infiniteQueryOptions.ts | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 packages/react-query/src/infiniteQueryOptions.ts diff --git a/packages/react-query/src/infiniteQueryOptions.ts b/packages/react-query/src/infiniteQueryOptions.ts new file mode 100644 index 00000000000..10e31a27894 --- /dev/null +++ b/packages/react-query/src/infiniteQueryOptions.ts @@ -0,0 +1,92 @@ +import type { UseInfiniteQueryOptions } from './types' +import type { DefaultError, QueryKey } from '@tanstack/query-core' + +export type UndefinedInitialDataInfiniteOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = UseInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam +> & { + initialData?: undefined +} + +export type DefinedInitialDataInfiniteOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = UseInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam +> & { + initialData: TQueryFnData | (() => TQueryFnData) +} + +export function infiniteQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam + >, +): UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam +> + +export function infiniteQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam + >, +): DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam +> + +export function infiniteQueryOptions(options: unknown) { + return options +} From e7cc229c57f2876dd4a751ab282fac87102e7823 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Tue, 18 Jul 2023 17:21:39 +0200 Subject: [PATCH 03/12] fix: add exports --- packages/react-query/src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-query/src/index.ts b/packages/react-query/src/index.ts index bb69c52e75d..8a8f705633e 100644 --- a/packages/react-query/src/index.ts +++ b/packages/react-query/src/index.ts @@ -8,7 +8,10 @@ export * from './types' export { useQueries } from './useQueries' export type { QueriesResults, QueriesOptions } from './useQueries' export { useQuery } from './useQuery' +export { useSuspenseQuery } from './useSuspenseQuery' +export { useSuspenseInfiniteQuery } from './useSuspenseInfiniteQuery' export { queryOptions } from './queryOptions' +export { infiniteQueryOptions } from './infiniteQueryOptions' export { QueryClientContext, QueryClientProvider, From 6bb924febb6ca1b0fe51c0cb5969c730b6351b9b Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Tue, 18 Jul 2023 17:28:28 +0200 Subject: [PATCH 04/12] feat: useSuspenseInfiniteQuery --- packages/react-query/src/types.ts | 10 ++--- packages/react-query/src/useInfiniteQuery.ts | 1 - .../src/useSuspenseInfiniteQuery.ts | 45 +++++++++++++++++++ packages/react-query/src/useSuspenseQuery.ts | 9 +++- 4 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 packages/react-query/src/useSuspenseInfiniteQuery.ts diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index 4736d667f9a..0f15071db10 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -64,20 +64,20 @@ export type UseQueryResult< TError = DefaultError, > = UseBaseQueryResult -export type DefinedUseBaseQueryResult< +export type DefinedUseQueryResult< TData = unknown, TError = DefaultError, > = DefinedQueryObserverResult -export type DefinedUseQueryResult< +export type UseInfiniteQueryResult< TData = unknown, TError = DefaultError, -> = DefinedUseBaseQueryResult +> = InfiniteQueryObserverResult -export type UseInfiniteQueryResult< +export type DefinedUseInfiniteQueryResult< TData = unknown, TError = DefaultError, -> = InfiniteQueryObserverResult +> = DefinedQueryObserverResult export interface UseMutationOptions< TData = unknown, diff --git a/packages/react-query/src/useInfiniteQuery.ts b/packages/react-query/src/useInfiniteQuery.ts index a320ccfd521..25874f5f1aa 100644 --- a/packages/react-query/src/useInfiniteQuery.ts +++ b/packages/react-query/src/useInfiniteQuery.ts @@ -10,7 +10,6 @@ import type { } from '@tanstack/query-core' import type { UseInfiniteQueryOptions, UseInfiniteQueryResult } from './types' -// HOOK export function useInfiniteQuery< TQueryFnData, TError = DefaultError, diff --git a/packages/react-query/src/useSuspenseInfiniteQuery.ts b/packages/react-query/src/useSuspenseInfiniteQuery.ts new file mode 100644 index 00000000000..4ea30dc1cb1 --- /dev/null +++ b/packages/react-query/src/useSuspenseInfiniteQuery.ts @@ -0,0 +1,45 @@ +'use client' +import { InfiniteQueryObserver } from '@tanstack/query-core' +import { useBaseQuery } from './useBaseQuery' +import type { QueryObserver } from '@tanstack/query-core' +import type { + DefaultError, + InfiniteData, + QueryClient, + QueryKey, +} from '@tanstack/query-core' +import type { DefinedUseInfiniteQueryResult } from './types' +import type { UseInfiniteQueryOptions } from './types' + +export function useSuspenseInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: Omit< + UseInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam + >, + 'enabled' | 'suspense' | 'throwOnError' | 'placeholderData' + >, + queryClient?: QueryClient, +): Omit, 'isPlaceholderData'> { + return useBaseQuery( + { + ...options, + enabled: true, + suspense: true, + throwOnError: true, + }, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + InfiniteQueryObserver as typeof QueryObserver, + queryClient, + ) as DefinedUseInfiniteQueryResult +} diff --git a/packages/react-query/src/useSuspenseQuery.ts b/packages/react-query/src/useSuspenseQuery.ts index 38951d1cd45..52f10eb5fe6 100644 --- a/packages/react-query/src/useSuspenseQuery.ts +++ b/packages/react-query/src/useSuspenseQuery.ts @@ -16,9 +16,14 @@ export function useSuspenseQuery< 'enabled' | 'suspense' | 'throwOnError' | 'placeholderData' >, queryClient?: QueryClient, -): DefinedUseQueryResult { +): Omit, 'isPlaceholderData'> { return useBaseQuery( - options, + { + ...options, + enabled: true, + suspense: true, + throwOnError: true, + }, QueryObserver, queryClient, ) as DefinedUseQueryResult From 28168a3205fbcb9611862a7f371e5b53cf7274e7 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Tue, 18 Jul 2023 17:31:06 +0200 Subject: [PATCH 05/12] feat: initialData overloads for useInfiniteQuery --- packages/react-query/src/useInfiniteQuery.ts | 51 +++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/packages/react-query/src/useInfiniteQuery.ts b/packages/react-query/src/useInfiniteQuery.ts index 25874f5f1aa..220b9858766 100644 --- a/packages/react-query/src/useInfiniteQuery.ts +++ b/packages/react-query/src/useInfiniteQuery.ts @@ -1,6 +1,20 @@ 'use client' import { InfiniteQueryObserver } from '@tanstack/query-core' import { useBaseQuery } from './useBaseQuery' +import { + DefinedInitialDataOptions, + UndefinedInitialDataOptions, +} from './queryOptions' +import type { + DefinedUseInfiniteQueryResult, + DefinedUseQueryResult, + UseQueryResult, +} from './types' +import type { + DefinedInitialDataInfiniteOptions, + UndefinedInitialDataInfiniteOptions, +} from './infiniteQueryOptions' +import type { UseInfiniteQueryOptions, UseInfiniteQueryResult } from './types' import type { DefaultError, InfiniteData, @@ -8,7 +22,42 @@ import type { QueryKey, QueryObserver, } from '@tanstack/query-core' -import type { UseInfiniteQueryOptions, UseInfiniteQueryResult } from './types' + +export function useInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam + >, + queryClient?: QueryClient, +): UseInfiniteQueryResult + +export function useInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam + >, + queryClient?: QueryClient, +): DefinedUseInfiniteQueryResult export function useInfiniteQuery< TQueryFnData, From bd6aa3ab0be4ca92ecc5ee767ad2a23f8ed4364d Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Tue, 18 Jul 2023 17:51:22 +0200 Subject: [PATCH 06/12] fix: types --- packages/query-core/src/types.ts | 10 ++++++-- .../react-query/src/infiniteQueryOptions.ts | 3 ++- packages/react-query/src/types.ts | 3 ++- packages/react-query/src/useInfiniteQuery.ts | 23 ++++++++----------- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 3658bed99b1..27aa5c054c3 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -585,14 +585,20 @@ export interface InfiniteQueryObserverSuccessResult< status: 'success' } +export type DefinedInfiniteQueryObserverResult< + TData = unknown, + TError = DefaultError, +> = + | InfiniteQueryObserverRefetchErrorResult + | InfiniteQueryObserverSuccessResult + export type InfiniteQueryObserverResult< TData = unknown, TError = DefaultError, > = | InfiniteQueryObserverLoadingErrorResult | InfiniteQueryObserverLoadingResult - | InfiniteQueryObserverRefetchErrorResult - | InfiniteQueryObserverSuccessResult + | DefinedInfiniteQueryObserverResult export type MutationKey = readonly unknown[] diff --git a/packages/react-query/src/infiniteQueryOptions.ts b/packages/react-query/src/infiniteQueryOptions.ts index 10e31a27894..ca138f4836a 100644 --- a/packages/react-query/src/infiniteQueryOptions.ts +++ b/packages/react-query/src/infiniteQueryOptions.ts @@ -1,3 +1,4 @@ +import type { InfiniteData } from '@tanstack/query-core' import type { UseInfiniteQueryOptions } from './types' import type { DefaultError, QueryKey } from '@tanstack/query-core' @@ -34,7 +35,7 @@ export type DefinedInitialDataInfiniteOptions< TQueryKey, TPageParam > & { - initialData: TQueryFnData | (() => TQueryFnData) + initialData: InfiniteData | (() => InfiniteData) } export function infiniteQueryOptions< diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index 0f15071db10..d5ffaef5984 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -2,6 +2,7 @@ import type { DefaultError, + DefinedInfiniteQueryObserverResult, DefinedQueryObserverResult, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, @@ -77,7 +78,7 @@ export type UseInfiniteQueryResult< export type DefinedUseInfiniteQueryResult< TData = unknown, TError = DefaultError, -> = DefinedQueryObserverResult +> = DefinedInfiniteQueryObserverResult export interface UseMutationOptions< TData = unknown, diff --git a/packages/react-query/src/useInfiniteQuery.ts b/packages/react-query/src/useInfiniteQuery.ts index 220b9858766..8b4e7df4386 100644 --- a/packages/react-query/src/useInfiniteQuery.ts +++ b/packages/react-query/src/useInfiniteQuery.ts @@ -1,20 +1,6 @@ 'use client' import { InfiniteQueryObserver } from '@tanstack/query-core' import { useBaseQuery } from './useBaseQuery' -import { - DefinedInitialDataOptions, - UndefinedInitialDataOptions, -} from './queryOptions' -import type { - DefinedUseInfiniteQueryResult, - DefinedUseQueryResult, - UseQueryResult, -} from './types' -import type { - DefinedInitialDataInfiniteOptions, - UndefinedInitialDataInfiniteOptions, -} from './infiniteQueryOptions' -import type { UseInfiniteQueryOptions, UseInfiniteQueryResult } from './types' import type { DefaultError, InfiniteData, @@ -22,6 +8,15 @@ import type { QueryKey, QueryObserver, } from '@tanstack/query-core' +import type { + DefinedUseInfiniteQueryResult, + UseInfiniteQueryOptions, + UseInfiniteQueryResult, +} from './types' +import type { + DefinedInitialDataInfiniteOptions, + UndefinedInitialDataInfiniteOptions, +} from './infiniteQueryOptions' export function useInfiniteQuery< TQueryFnData, From 3f9f6d020755af2c90fe350e1d7d1e997c9921fd Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Tue, 18 Jul 2023 17:57:40 +0200 Subject: [PATCH 07/12] chore: stabilize test we sometimes get failureCount: 2, but it doesn't matter here (timing issue) --- packages/react-query/src/__tests__/useQuery.test.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index 7c55bce5fcf..3b39e80a421 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -5422,11 +5422,8 @@ describe('useQuery', () => { const rendered = renderWithClient(queryClient, ) await waitFor(() => - rendered.getByText( - 'status: pending, fetchStatus: fetching, failureCount: 1', - ), + rendered.getByText(/status: pending, fetchStatus: fetching/i), ) - await waitFor(() => rendered.getByText('failureReason: failed1')) const onlineMock = mockOnlineManagerIsOnline(false) window.dispatchEvent(new Event('offline')) From a8ae890bc891a7f4a82ff8c3a8fa395f2d1dcd06 Mon Sep 17 00:00:00 2001 From: Jonghyeon Ko Date: Fri, 21 Jul 2023 16:56:44 +0900 Subject: [PATCH 08/12] fix: types for useSuspenseQuery (#5755) --- packages/react-query/src/types.ts | 16 ++++++++++++++++ packages/react-query/src/useSuspenseQuery.ts | 12 ++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index d5ffaef5984..a2a21870461 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -12,6 +12,7 @@ import type { QueryKey, QueryObserverOptions, QueryObserverResult, + QueryObserverSuccessResult, WithRequired, } from '@tanstack/query-core' @@ -36,6 +37,16 @@ export interface UseQueryOptions< 'queryKey' > {} +export interface UseSuspenseQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> extends Omit< + UseQueryOptions, + 'enabled' | 'suspense' | 'throwOnError' | 'placeholderData' + > {} + export interface UseInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, @@ -65,6 +76,11 @@ export type UseQueryResult< TError = DefaultError, > = UseBaseQueryResult +export type UseSuspenseQueryResult< + TData = unknown, + TError = DefaultError, +> = Omit, 'isPlaceholderData'> + export type DefinedUseQueryResult< TData = unknown, TError = DefaultError, diff --git a/packages/react-query/src/useSuspenseQuery.ts b/packages/react-query/src/useSuspenseQuery.ts index 52f10eb5fe6..1b25892542e 100644 --- a/packages/react-query/src/useSuspenseQuery.ts +++ b/packages/react-query/src/useSuspenseQuery.ts @@ -1,9 +1,8 @@ 'use client' import { QueryObserver } from '@tanstack/query-core' import { useBaseQuery } from './useBaseQuery' -import type { UseQueryOptions } from './types' +import type { UseSuspenseQueryOptions, UseSuspenseQueryResult } from './types' import type { DefaultError, QueryClient, QueryKey } from '@tanstack/query-core' -import type { DefinedUseQueryResult } from './types' export function useSuspenseQuery< TQueryFnData = unknown, @@ -11,12 +10,9 @@ export function useSuspenseQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: Omit< - UseQueryOptions, - 'enabled' | 'suspense' | 'throwOnError' | 'placeholderData' - >, + options: UseSuspenseQueryOptions, queryClient?: QueryClient, -): Omit, 'isPlaceholderData'> { +): UseSuspenseQueryResult { return useBaseQuery( { ...options, @@ -26,5 +22,5 @@ export function useSuspenseQuery< }, QueryObserver, queryClient, - ) as DefinedUseQueryResult + ) as UseSuspenseQueryResult } From bd7126e53a1c1f8c09e999c7c4eae87da743c8d5 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 21 Jul 2023 10:13:26 +0200 Subject: [PATCH 09/12] docs: suspense --- docs/react/guides/suspense.md | 47 ++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/docs/react/guides/suspense.md b/docs/react/guides/suspense.md index 7e944e6b5ed..2d8ac8d8f08 100644 --- a/docs/react/guides/suspense.md +++ b/docs/react/guides/suspense.md @@ -3,9 +3,7 @@ id: suspense title: Suspense --- -> NOTE: Suspense mode for React Query is experimental, same as Suspense for data fetching itself. These APIs WILL change and should not be used in production unless you lock both your React and React Query versions to patch-level versions that are compatible with each other. - -React Query can also be used with React's new Suspense for Data Fetching API's. To enable this mode, you can set either the global or query level config's `suspense` option to `true`. +React Query can also be used with React's Suspense for Data Fetching API's. To enable this mode, you can set either the global or query level config's `suspense` option to `true`. Global configuration: @@ -98,10 +96,53 @@ const App: React.FC = () => { } ``` +## useSuspenseQuery + +You can also use the dedicated `useSuspenseQuery` hook to enable suspense mode for a query: + +```tsx +import { useSuspenseQuery } from '@tanstack/react-query' + +const { data } = useSuspenseQuery({ queryKey, queryFn }) +``` + +This has the same effect as setting the `suspense` option to `true` in the query config, but it works better in TypeScript, because `data` is guaranteed to be defined (as errors and loading states are handled by Suspense- and ErrorBoundaries). + +On the flip side, you therefore can't conditionally enable / disable the Query. `placeholderData` also doesn't exist for this Query. To prevent the UI from being replaced by a fallback during an update, wrap your updates that change the QueryKey into [startTransition](https://react.dev/reference/react/Suspense#preventing-unwanted-fallbacks). + ## Fetch-on-render vs Render-as-you-fetch Out of the box, React Query in `suspense` mode works really well as a **Fetch-on-render** solution with no additional configuration. This means that when your components attempt to mount, they will trigger query fetching and suspend, but only once you have imported them and mounted them. If you want to take it to the next level and implement a **Render-as-you-fetch** model, we recommend implementing [Prefetching](../guides/prefetching) on routing callbacks and/or user interactions events to start loading queries before they are mounted and hopefully even before you start importing or mounting their parent components. +## Suspense on the Server with streaming + +If you are using `NextJs`, you can use our **experimental** integration for Suspense on the Server: `@tanstack/react-query-next-experimental`. This package will allow you to fetch data on the server (in a client component) by just calling `useQuery` (with `suspense: true`) or `useSuspenseQuery` in your component. Results will then be streamed from the server to the client as SuspenseBoundaries resolve. + +To achieve this, wrap your app in the `ReactQueryStreamedHydration` component: + +```tsx +// app/providers.tsx +'use client' + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import * as React from 'react' +import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental' + +export function Providers(props: { children: React.ReactNode }) { + const [queryClient] = React.useState(() => new QueryClient()) + + return ( + + + {props.children} + + + ) +} +``` + +For more information, check out the [NextJs Suspense Streaming Example](../examples/react/nextjs-suspense-streaming). + ## Further reading For tips on using suspense option, check the [Suspensive React Query Package](../community/suspensive-react-query) from the Community Resources. From cd244aa448d49cd35cb2c0883bf47c1059f3abb2 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 21 Jul 2023 10:24:03 +0200 Subject: [PATCH 10/12] docs: api reference --- .../reference/useSuspenseInfiniteQuery.md | 23 +++++++++++++++++++ docs/react/reference/useSuspenseQuery.md | 23 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 docs/react/reference/useSuspenseInfiniteQuery.md create mode 100644 docs/react/reference/useSuspenseQuery.md diff --git a/docs/react/reference/useSuspenseInfiniteQuery.md b/docs/react/reference/useSuspenseInfiniteQuery.md new file mode 100644 index 00000000000..9649a189d78 --- /dev/null +++ b/docs/react/reference/useSuspenseInfiniteQuery.md @@ -0,0 +1,23 @@ +--- +id: useSuspenseInfiniteQuery +title: useSuspenseInfiniteQuery +--- + +```tsx +const result = useSuspenseInfiniteQuery(options) +``` + +**Options** + +The same as for [useInfiniteQuery](../reference/useInfiniteQuery), except for: +- `suspense` +- `throwOnError` +- `enabled` +- `placeholderData` + +**Returns** + +Same object as [useInfiniteQuery](../reference/useInfiniteQuery), except for: +- `isPlaceholderData` is missing +- `status` is always `success` + - the derived flags are set accordingly. diff --git a/docs/react/reference/useSuspenseQuery.md b/docs/react/reference/useSuspenseQuery.md new file mode 100644 index 00000000000..c716093699a --- /dev/null +++ b/docs/react/reference/useSuspenseQuery.md @@ -0,0 +1,23 @@ +--- +id: useSuspenseQuery +title: useSuspenseQuery +--- + +```tsx +const result = useSuspenseQuery(options) +``` + +**Options** + +The same as for [useQuery](../reference/useQuery), except for: +- `suspense` +- `throwOnError` +- `enabled` +- `placeholderData` + +**Returns** + +Same object as [useQuery](../reference/useQuery), except for: +- `isPlaceholderData` is missing +- `status` is always `success` + - the derived flags are set accordingly. From 38e92a4c0d5ebaf84725cbe2d1c0cd96bd86633a Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 21 Jul 2023 11:56:59 +0200 Subject: [PATCH 11/12] docs: useSuspenseQuery in examples --- examples/react/nextjs-suspense-streaming/src/app/page.tsx | 5 ++--- examples/react/suspense/src/components/Project.jsx | 4 ++-- examples/react/suspense/src/components/Projects.jsx | 4 ++-- examples/react/suspense/src/index.jsx | 1 - 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/examples/react/nextjs-suspense-streaming/src/app/page.tsx b/examples/react/nextjs-suspense-streaming/src/app/page.tsx index dfe62c1c424..7d46a105ae5 100644 --- a/examples/react/nextjs-suspense-streaming/src/app/page.tsx +++ b/examples/react/nextjs-suspense-streaming/src/app/page.tsx @@ -1,5 +1,5 @@ 'use client' -import { useQuery } from '@tanstack/react-query' +import { useSuspenseQuery } from '@tanstack/react-query' import { Suspense } from 'react' // export const runtime = "edge"; // 'nodejs' (default) | 'edge' @@ -15,7 +15,7 @@ function getBaseURL() { } const baseUrl = getBaseURL() function useWaitQuery(props: { wait: number }) { - const query = useQuery({ + const query = useSuspenseQuery({ queryKey: ['wait', props.wait], queryFn: async () => { const path = `/api/wait?wait=${props.wait}` @@ -29,7 +29,6 @@ function useWaitQuery(props: { wait: number }) { ).json() return res }, - suspense: true, }) return [query.data as string, query] as const diff --git a/examples/react/suspense/src/components/Project.jsx b/examples/react/suspense/src/components/Project.jsx index 798d09b3fcf..a3ffdb4b090 100644 --- a/examples/react/suspense/src/components/Project.jsx +++ b/examples/react/suspense/src/components/Project.jsx @@ -1,5 +1,5 @@ import React from 'react' -import { useQuery } from '@tanstack/react-query' +import { useSuspenseQuery } from '@tanstack/react-query' import Button from './Button' import Spinner from './Spinner' @@ -7,7 +7,7 @@ import Spinner from './Spinner' import { fetchProject } from '../queries' export default function Project({ activeProject, setActiveProject }) { - const { data, isFetching } = useQuery({ + const { data, isFetching } = useSuspenseQuery({ queryKey: ['project', activeProject], queryFn: () => fetchProject(activeProject), }) diff --git a/examples/react/suspense/src/components/Projects.jsx b/examples/react/suspense/src/components/Projects.jsx index fad6598aa82..5f652d22a3e 100644 --- a/examples/react/suspense/src/components/Projects.jsx +++ b/examples/react/suspense/src/components/Projects.jsx @@ -1,5 +1,5 @@ import React from 'react' -import { useQuery, useQueryClient } from '@tanstack/react-query' +import { useSuspenseQuery, useQueryClient } from '@tanstack/react-query' import Button from './Button' import Spinner from './Spinner' @@ -8,7 +8,7 @@ import { fetchProjects, fetchProject } from '../queries' export default function Projects({ setActiveProject }) { const queryClient = useQueryClient() - const { data, isFetching } = useQuery({ + const { data, isFetching } = useSuspenseQuery({ queryKey: ['projects'], queryFn: fetchProjects, }) diff --git a/examples/react/suspense/src/index.jsx b/examples/react/suspense/src/index.jsx index 1b3622836be..ee00cbca8f2 100755 --- a/examples/react/suspense/src/index.jsx +++ b/examples/react/suspense/src/index.jsx @@ -20,7 +20,6 @@ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 0, - suspense: true, }, }, }) From d5c2a8e91a777063aea696dbf655027226e7927a Mon Sep 17 00:00:00 2001 From: Jonghyeon Ko Date: Sat, 22 Jul 2023 19:22:46 +0900 Subject: [PATCH 12/12] fix: types for useSuspenseInfiniteQuery (#5766) --- packages/react-query/src/types.ts | 25 +++++++++++++++ .../src/useSuspenseInfiniteQuery.ts | 32 ++++++++++--------- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index a2a21870461..532e3a90576 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -6,6 +6,7 @@ import type { DefinedQueryObserverResult, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, + InfiniteQueryObserverSuccessResult, MutateFunction, MutationObserverOptions, MutationObserverResult, @@ -66,6 +67,25 @@ export interface UseInfiniteQueryOptions< 'queryKey' > {} +export interface UseSuspenseInfiniteQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> extends Omit< + UseInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam + >, + 'enabled' | 'suspense' | 'throwOnError' | 'placeholderData' + > {} + export type UseBaseQueryResult< TData = unknown, TError = DefaultError, @@ -96,6 +116,11 @@ export type DefinedUseInfiniteQueryResult< TError = DefaultError, > = DefinedInfiniteQueryObserverResult +export type UseSuspenseInfiniteQueryResult< + TData = unknown, + TError = DefaultError, +> = Omit, 'isPlaceholderData'> + export interface UseMutationOptions< TData = unknown, TError = DefaultError, diff --git a/packages/react-query/src/useSuspenseInfiniteQuery.ts b/packages/react-query/src/useSuspenseInfiniteQuery.ts index 4ea30dc1cb1..61450eb463f 100644 --- a/packages/react-query/src/useSuspenseInfiniteQuery.ts +++ b/packages/react-query/src/useSuspenseInfiniteQuery.ts @@ -1,15 +1,20 @@ 'use client' import { InfiniteQueryObserver } from '@tanstack/query-core' import { useBaseQuery } from './useBaseQuery' -import type { QueryObserver } from '@tanstack/query-core' +import type { + InfiniteQueryObserverSuccessResult, + QueryObserver, +} from '@tanstack/query-core' import type { DefaultError, InfiniteData, QueryClient, QueryKey, } from '@tanstack/query-core' -import type { DefinedUseInfiniteQueryResult } from './types' -import type { UseInfiniteQueryOptions } from './types' +import type { + UseSuspenseInfiniteQueryOptions, + UseSuspenseInfiniteQueryResult, +} from './types' export function useSuspenseInfiniteQuery< TQueryFnData, @@ -18,19 +23,16 @@ export function useSuspenseInfiniteQuery< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: Omit< - UseInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey, - TPageParam - >, - 'enabled' | 'suspense' | 'throwOnError' | 'placeholderData' + options: UseSuspenseInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam >, queryClient?: QueryClient, -): Omit, 'isPlaceholderData'> { +): UseSuspenseInfiniteQueryResult { return useBaseQuery( { ...options, @@ -41,5 +43,5 @@ export function useSuspenseInfiniteQuery< // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion InfiniteQueryObserver as typeof QueryObserver, queryClient, - ) as DefinedUseInfiniteQueryResult + ) as InfiniteQueryObserverSuccessResult }