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 b25d049c73..0a673b9a6f 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 0000000000..38951d1cd4 --- /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 0000000000..10e31a2789 --- /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 bb69c52e75..8a8f705633 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 4736d667f9..0f15071db1 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 a320ccfd52..25874f5f1a 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 0000000000..4ea30dc1cb --- /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 38951d1cd4..52f10eb5fe 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 25874f5f1a..220b985876 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 3658bed99b..27aa5c054c 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 10e31a2789..ca138f4836 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 0f15071db1..d5ffaef598 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 220b985876..8b4e7df438 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 7c55bce5fc..3b39e80a42 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 d5ffaef598..a2a2187046 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 52f10eb5fe..1b25892542 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 7e944e6b5e..2d8ac8d8f0 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 0000000000..9649a189d7 --- /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 0000000000..c716093699 --- /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 dfe62c1c42..7d46a105ae 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 798d09b3fc..a3ffdb4b09 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 fad6598aa8..5f652d22a3 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 1b3622836b..ee00cbca8f 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 a2a2187046..532e3a9057 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 4ea30dc1cb..61450eb463 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 }