@suspensive/react-query-4 3.19.6 → 3.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -84,13 +84,14 @@ function QueriesHydration(_x) {
|
|
|
84
84
|
function _QueriesHydration() {
|
|
85
85
|
_QueriesHydration = require_asyncToGenerator._asyncToGenerator(function* (_ref) {
|
|
86
86
|
let { queries, children, queryClient = new _tanstack_react_query.QueryClient(), skipSsrOnError = true, timeout } = _ref, props = require_objectWithoutProperties._objectWithoutProperties(_ref, _excluded);
|
|
87
|
-
const timeoutController = timeout != null && timeout >= 0 ? createTimeoutController(timeout
|
|
87
|
+
const timeoutController = timeout != null && timeout >= 0 ? createTimeoutController(timeout) : void 0;
|
|
88
88
|
try {
|
|
89
89
|
const queriesPromise = Promise.all(queries.map((query) => "getNextPageParam" in query ? queryClient.fetchInfiniteQuery(query) : queryClient.fetchQuery(query)));
|
|
90
90
|
yield timeoutController != null ? Promise.race([queriesPromise, timeoutController.promise]) : queriesPromise;
|
|
91
91
|
timeoutController === null || timeoutController === void 0 || timeoutController.clear();
|
|
92
92
|
} catch (_unused) {
|
|
93
93
|
timeoutController === null || timeoutController === void 0 || timeoutController.clear();
|
|
94
|
+
queries.forEach((query) => void queryClient.cancelQueries(query));
|
|
94
95
|
if (skipSsrOnError) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ClientOnly.ClientOnly, {
|
|
95
96
|
fallback: skipSsrOnError === true ? void 0 : skipSsrOnError.fallback,
|
|
96
97
|
children
|
|
@@ -103,11 +104,11 @@ function _QueriesHydration() {
|
|
|
103
104
|
});
|
|
104
105
|
return _QueriesHydration.apply(this, arguments);
|
|
105
106
|
}
|
|
106
|
-
const createTimeoutController = (ms
|
|
107
|
+
const createTimeoutController = (ms) => {
|
|
107
108
|
let timerId;
|
|
108
109
|
return {
|
|
109
110
|
promise: new Promise((_, reject) => {
|
|
110
|
-
timerId = setTimeout(() => reject(new Error(
|
|
111
|
+
timerId = setTimeout(() => reject(/* @__PURE__ */ new Error(`QueriesHydration: timeout after ${ms} ms`)), ms);
|
|
111
112
|
}),
|
|
112
113
|
clear: () => timerId != null && clearTimeout(timerId)
|
|
113
114
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueriesHydration.cjs","names":["QueryClient","ClientOnly","Hydrate"],"sources":["../src/QueriesHydration.tsx"],"sourcesContent":["import {\n Hydrate,\n type HydrateProps,\n type OmitKeyof,\n QueryClient,\n type QueryOptions,\n type UseInfiniteQueryOptions,\n type WithRequired,\n dehydrate,\n} from '@tanstack/react-query'\nimport type { ReactNode } from 'react'\nimport { ClientOnly } from './components/ClientOnly'\n\n/**\n * A server component that fetches multiple queries on the server and hydrates them to the client.\n *\n * @experimental This component is experimental and may be changed or removed in the future.\n *\n * @description\n * QueriesHydration is designed for React Server Components (RSC).\n * It pre-fetches multiple queries on the server side and automatically hydrates\n * the data to the client, enabling seamless data synchronization between server and client.\n *\n * When errors occur during server-side fetching, the component gracefully falls back\n * to client-side rendering, ensuring your application remains resilient.\n *\n * @example\n * ```tsx\n * // app/page.tsx (Server Component)\n * import { Suspense } from 'react'\n * import { QueriesHydration } from '@suspensive/react-query'\n * import { queryOptions } from '@tanstack/react-query'\n *\n * const userQueryOptions = (userId: string) => queryOptions({\n * queryKey: ['user', userId],\n * queryFn: () => fetchUser(userId)\n * })\n *\n * const postsQueryOptions = () => queryOptions({\n * queryKey: ['posts'],\n * queryFn: () => fetchPosts()\n * })\n *\n * export default function Page({ userId }: { userId: string }) {\n * return (\n * <>\n * <Suspense fallback={<div>Loading user...</div>}>\n * <QueriesHydration queries={[userQueryOptions(userId)]}>\n * <UserProfile />\n * </QueriesHydration>\n * </Suspense>\n *\n * <Suspense fallback={<div>Loading posts...</div>}>\n * <QueriesHydration queries={[postsQueryOptions()]}>\n * <PostsList />\n * </QueriesHydration>\n * </Suspense>\n * </>\n * )\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With custom error fallback\n * <Suspense fallback={<div>Loading user...</div>}>\n * <QueriesHydration\n * queries={[userQueryOptions(userId)]}\n * skipSsrOnError={{ fallback: <div>Fetching on client...</div> }}\n * >\n * <UserProfile />\n * </QueriesHydration>\n * </Suspense>\n * ```\n *\n * @see {@link https://suspensive.org/docs/react-query/QueriesHydration Documentation}\n */\nexport async function QueriesHydration({\n queries,\n children,\n queryClient = new QueryClient(),\n skipSsrOnError = true,\n timeout,\n ...props\n}: {\n /**\n * The QueryClient instance to use for fetching queries.\n */\n queryClient?: QueryClient\n /**\n * An array of query options or infinite query options to be fetched on the server. Each query must include a `queryKey`.\n * You can mix regular queries and infinite queries in the same array.\n */\n queries: (\n | WithRequired<QueryOptions<any, any, any, any>, 'queryKey'>\n | WithRequired<UseInfiniteQueryOptions<any, any, any, any, any>, 'queryKey'>\n )[]\n /**\n * Controls error handling behavior:\n * - `true` (default): Skips SSR and falls back to client-side rendering when server fetch fails\n * - `false`: Proceeds with SSR without hydration (retry fetching on client component server rendering)\n * - `{ fallback: ReactNode }`: Skips SSR with custom fallback UI during client-side rendering\n */\n skipSsrOnError?:\n | boolean\n | {\n fallback: ReactNode\n }\n /**\n * The timeout in milliseconds for the query.\n * If the query takes longer than the timeout, it will be considered as an error.\n * When not set, no timeout is applied.\n */\n timeout?: number\n} & OmitKeyof<HydrateProps, 'state'>) {\n const timeoutController
|
|
1
|
+
{"version":3,"file":"QueriesHydration.cjs","names":["QueryClient","ClientOnly","Hydrate"],"sources":["../src/QueriesHydration.tsx"],"sourcesContent":["import {\n Hydrate,\n type HydrateProps,\n type OmitKeyof,\n QueryClient,\n type QueryOptions,\n type UseInfiniteQueryOptions,\n type WithRequired,\n dehydrate,\n} from '@tanstack/react-query'\nimport type { ReactNode } from 'react'\nimport { ClientOnly } from './components/ClientOnly'\n\n/**\n * A server component that fetches multiple queries on the server and hydrates them to the client.\n *\n * @experimental This component is experimental and may be changed or removed in the future.\n *\n * @description\n * QueriesHydration is designed for React Server Components (RSC).\n * It pre-fetches multiple queries on the server side and automatically hydrates\n * the data to the client, enabling seamless data synchronization between server and client.\n *\n * When errors occur during server-side fetching, the component gracefully falls back\n * to client-side rendering, ensuring your application remains resilient.\n *\n * @example\n * ```tsx\n * // app/page.tsx (Server Component)\n * import { Suspense } from 'react'\n * import { QueriesHydration } from '@suspensive/react-query'\n * import { queryOptions } from '@tanstack/react-query'\n *\n * const userQueryOptions = (userId: string) => queryOptions({\n * queryKey: ['user', userId],\n * queryFn: () => fetchUser(userId)\n * })\n *\n * const postsQueryOptions = () => queryOptions({\n * queryKey: ['posts'],\n * queryFn: () => fetchPosts()\n * })\n *\n * export default function Page({ userId }: { userId: string }) {\n * return (\n * <>\n * <Suspense fallback={<div>Loading user...</div>}>\n * <QueriesHydration queries={[userQueryOptions(userId)]}>\n * <UserProfile />\n * </QueriesHydration>\n * </Suspense>\n *\n * <Suspense fallback={<div>Loading posts...</div>}>\n * <QueriesHydration queries={[postsQueryOptions()]}>\n * <PostsList />\n * </QueriesHydration>\n * </Suspense>\n * </>\n * )\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With custom error fallback\n * <Suspense fallback={<div>Loading user...</div>}>\n * <QueriesHydration\n * queries={[userQueryOptions(userId)]}\n * skipSsrOnError={{ fallback: <div>Fetching on client...</div> }}\n * >\n * <UserProfile />\n * </QueriesHydration>\n * </Suspense>\n * ```\n *\n * @see {@link https://suspensive.org/docs/react-query/QueriesHydration Documentation}\n */\nexport async function QueriesHydration({\n queries,\n children,\n queryClient = new QueryClient(),\n skipSsrOnError = true,\n timeout,\n ...props\n}: {\n /**\n * The QueryClient instance to use for fetching queries.\n */\n queryClient?: QueryClient\n /**\n * An array of query options or infinite query options to be fetched on the server. Each query must include a `queryKey`.\n * You can mix regular queries and infinite queries in the same array.\n */\n queries: (\n | WithRequired<QueryOptions<any, any, any, any>, 'queryKey'>\n | WithRequired<UseInfiniteQueryOptions<any, any, any, any, any>, 'queryKey'>\n )[]\n /**\n * Controls error handling behavior:\n * - `true` (default): Skips SSR and falls back to client-side rendering when server fetch fails\n * - `false`: Proceeds with SSR without hydration (retry fetching on client component server rendering)\n * - `{ fallback: ReactNode }`: Skips SSR with custom fallback UI during client-side rendering\n */\n skipSsrOnError?:\n | boolean\n | {\n fallback: ReactNode\n }\n /**\n * The timeout in milliseconds for the query.\n * If the query takes longer than the timeout, it will be considered as an error.\n * When not set, no timeout is applied.\n */\n timeout?: number\n} & OmitKeyof<HydrateProps, 'state'>) {\n const timeoutController = timeout != null && timeout >= 0 ? createTimeoutController(timeout) : undefined\n try {\n const queriesPromise = Promise.all(\n queries.map((query) =>\n 'getNextPageParam' in query ? queryClient.fetchInfiniteQuery(query) : queryClient.fetchQuery(query)\n )\n )\n await (timeoutController != null ? Promise.race([queriesPromise, timeoutController.promise]) : queriesPromise)\n timeoutController?.clear()\n } catch {\n timeoutController?.clear()\n queries.forEach((query) => void queryClient.cancelQueries(query))\n if (skipSsrOnError) {\n return (\n <ClientOnly fallback={skipSsrOnError === true ? undefined : skipSsrOnError.fallback}>{children}</ClientOnly>\n )\n }\n }\n return (\n <Hydrate {...props} state={dehydrate(queryClient)}>\n {children}\n </Hydrate>\n )\n}\n\nconst createTimeoutController = (ms: number) => {\n let timerId: ReturnType<typeof setTimeout> | undefined\n return {\n promise: new Promise<never>((_, reject) => {\n timerId = setTimeout(() => reject(new Error(`QueriesHydration: timeout after ${ms} ms`)), ms)\n }),\n clear: () => timerId != null && clearTimeout(timerId),\n }\n}\n"],"mappings":";;;;;;;;;;CA8EE;CACA;CACA;CACA;CACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AALF,SAAsB,iBAAiB;;;;iFAqCD;MArCC,EACrC,SACA,UACA,cAAc,IAAIA,mCAAa,EAC/B,iBAAiB,MACjB,kBACG;EAgCH,MAAM,oBAAoB,WAAW,QAAQ,WAAW,IAAI,wBAAwB,QAAQ,GAAG;AAC/F,MAAI;GACF,MAAM,iBAAiB,QAAQ,IAC7B,QAAQ,KAAK,UACX,sBAAsB,QAAQ,YAAY,mBAAmB,MAAM,GAAG,YAAY,WAAW,MAAM,CACpG,CACF;AACD,SAAO,qBAAqB,OAAO,QAAQ,KAAK,CAAC,gBAAgB,kBAAkB,QAAQ,CAAC,GAAG;AAC/F,mFAAmB,OAAO;oBACpB;AACN,mFAAmB,OAAO;AAC1B,WAAQ,SAAS,UAAU,KAAK,YAAY,cAAc,MAAM,CAAC;AACjE,OAAI,eACF,QACE,2CAACC,+BAAD;IAAY,UAAU,mBAAmB,OAAO,SAAY,eAAe;IAAW;IAAsB;;AAIlH,SACE,2CAACC,6GAAY;GAAO,4CAAiB,YAAY;GAC9C;IACO;;;;AAId,MAAM,2BAA2B,OAAe;CAC9C,IAAI;AACJ,QAAO;EACL,SAAS,IAAI,SAAgB,GAAG,WAAW;AACzC,aAAU,iBAAiB,uBAAO,IAAI,MAAM,mCAAmC,GAAG,KAAK,CAAC,EAAE,GAAG;IAC7F;EACF,aAAa,WAAW,QAAQ,aAAa,QAAQ;EACtD"}
|
|
@@ -83,13 +83,14 @@ function QueriesHydration(_x) {
|
|
|
83
83
|
function _QueriesHydration() {
|
|
84
84
|
_QueriesHydration = _asyncToGenerator(function* (_ref) {
|
|
85
85
|
let { queries, children, queryClient = new QueryClient(), skipSsrOnError = true, timeout } = _ref, props = _objectWithoutProperties(_ref, _excluded);
|
|
86
|
-
const timeoutController = timeout != null && timeout >= 0 ? createTimeoutController(timeout
|
|
86
|
+
const timeoutController = timeout != null && timeout >= 0 ? createTimeoutController(timeout) : void 0;
|
|
87
87
|
try {
|
|
88
88
|
const queriesPromise = Promise.all(queries.map((query) => "getNextPageParam" in query ? queryClient.fetchInfiniteQuery(query) : queryClient.fetchQuery(query)));
|
|
89
89
|
yield timeoutController != null ? Promise.race([queriesPromise, timeoutController.promise]) : queriesPromise;
|
|
90
90
|
timeoutController === null || timeoutController === void 0 || timeoutController.clear();
|
|
91
91
|
} catch (_unused) {
|
|
92
92
|
timeoutController === null || timeoutController === void 0 || timeoutController.clear();
|
|
93
|
+
queries.forEach((query) => void queryClient.cancelQueries(query));
|
|
93
94
|
if (skipSsrOnError) return /* @__PURE__ */ jsx(ClientOnly, {
|
|
94
95
|
fallback: skipSsrOnError === true ? void 0 : skipSsrOnError.fallback,
|
|
95
96
|
children
|
|
@@ -102,11 +103,11 @@ function _QueriesHydration() {
|
|
|
102
103
|
});
|
|
103
104
|
return _QueriesHydration.apply(this, arguments);
|
|
104
105
|
}
|
|
105
|
-
const createTimeoutController = (ms
|
|
106
|
+
const createTimeoutController = (ms) => {
|
|
106
107
|
let timerId;
|
|
107
108
|
return {
|
|
108
109
|
promise: new Promise((_, reject) => {
|
|
109
|
-
timerId = setTimeout(() => reject(new Error(
|
|
110
|
+
timerId = setTimeout(() => reject(/* @__PURE__ */ new Error(`QueriesHydration: timeout after ${ms} ms`)), ms);
|
|
110
111
|
}),
|
|
111
112
|
clear: () => timerId != null && clearTimeout(timerId)
|
|
112
113
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueriesHydration.mjs","names":[],"sources":["../src/QueriesHydration.tsx"],"sourcesContent":["import {\n Hydrate,\n type HydrateProps,\n type OmitKeyof,\n QueryClient,\n type QueryOptions,\n type UseInfiniteQueryOptions,\n type WithRequired,\n dehydrate,\n} from '@tanstack/react-query'\nimport type { ReactNode } from 'react'\nimport { ClientOnly } from './components/ClientOnly'\n\n/**\n * A server component that fetches multiple queries on the server and hydrates them to the client.\n *\n * @experimental This component is experimental and may be changed or removed in the future.\n *\n * @description\n * QueriesHydration is designed for React Server Components (RSC).\n * It pre-fetches multiple queries on the server side and automatically hydrates\n * the data to the client, enabling seamless data synchronization between server and client.\n *\n * When errors occur during server-side fetching, the component gracefully falls back\n * to client-side rendering, ensuring your application remains resilient.\n *\n * @example\n * ```tsx\n * // app/page.tsx (Server Component)\n * import { Suspense } from 'react'\n * import { QueriesHydration } from '@suspensive/react-query'\n * import { queryOptions } from '@tanstack/react-query'\n *\n * const userQueryOptions = (userId: string) => queryOptions({\n * queryKey: ['user', userId],\n * queryFn: () => fetchUser(userId)\n * })\n *\n * const postsQueryOptions = () => queryOptions({\n * queryKey: ['posts'],\n * queryFn: () => fetchPosts()\n * })\n *\n * export default function Page({ userId }: { userId: string }) {\n * return (\n * <>\n * <Suspense fallback={<div>Loading user...</div>}>\n * <QueriesHydration queries={[userQueryOptions(userId)]}>\n * <UserProfile />\n * </QueriesHydration>\n * </Suspense>\n *\n * <Suspense fallback={<div>Loading posts...</div>}>\n * <QueriesHydration queries={[postsQueryOptions()]}>\n * <PostsList />\n * </QueriesHydration>\n * </Suspense>\n * </>\n * )\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With custom error fallback\n * <Suspense fallback={<div>Loading user...</div>}>\n * <QueriesHydration\n * queries={[userQueryOptions(userId)]}\n * skipSsrOnError={{ fallback: <div>Fetching on client...</div> }}\n * >\n * <UserProfile />\n * </QueriesHydration>\n * </Suspense>\n * ```\n *\n * @see {@link https://suspensive.org/docs/react-query/QueriesHydration Documentation}\n */\nexport async function QueriesHydration({\n queries,\n children,\n queryClient = new QueryClient(),\n skipSsrOnError = true,\n timeout,\n ...props\n}: {\n /**\n * The QueryClient instance to use for fetching queries.\n */\n queryClient?: QueryClient\n /**\n * An array of query options or infinite query options to be fetched on the server. Each query must include a `queryKey`.\n * You can mix regular queries and infinite queries in the same array.\n */\n queries: (\n | WithRequired<QueryOptions<any, any, any, any>, 'queryKey'>\n | WithRequired<UseInfiniteQueryOptions<any, any, any, any, any>, 'queryKey'>\n )[]\n /**\n * Controls error handling behavior:\n * - `true` (default): Skips SSR and falls back to client-side rendering when server fetch fails\n * - `false`: Proceeds with SSR without hydration (retry fetching on client component server rendering)\n * - `{ fallback: ReactNode }`: Skips SSR with custom fallback UI during client-side rendering\n */\n skipSsrOnError?:\n | boolean\n | {\n fallback: ReactNode\n }\n /**\n * The timeout in milliseconds for the query.\n * If the query takes longer than the timeout, it will be considered as an error.\n * When not set, no timeout is applied.\n */\n timeout?: number\n} & OmitKeyof<HydrateProps, 'state'>) {\n const timeoutController
|
|
1
|
+
{"version":3,"file":"QueriesHydration.mjs","names":[],"sources":["../src/QueriesHydration.tsx"],"sourcesContent":["import {\n Hydrate,\n type HydrateProps,\n type OmitKeyof,\n QueryClient,\n type QueryOptions,\n type UseInfiniteQueryOptions,\n type WithRequired,\n dehydrate,\n} from '@tanstack/react-query'\nimport type { ReactNode } from 'react'\nimport { ClientOnly } from './components/ClientOnly'\n\n/**\n * A server component that fetches multiple queries on the server and hydrates them to the client.\n *\n * @experimental This component is experimental and may be changed or removed in the future.\n *\n * @description\n * QueriesHydration is designed for React Server Components (RSC).\n * It pre-fetches multiple queries on the server side and automatically hydrates\n * the data to the client, enabling seamless data synchronization between server and client.\n *\n * When errors occur during server-side fetching, the component gracefully falls back\n * to client-side rendering, ensuring your application remains resilient.\n *\n * @example\n * ```tsx\n * // app/page.tsx (Server Component)\n * import { Suspense } from 'react'\n * import { QueriesHydration } from '@suspensive/react-query'\n * import { queryOptions } from '@tanstack/react-query'\n *\n * const userQueryOptions = (userId: string) => queryOptions({\n * queryKey: ['user', userId],\n * queryFn: () => fetchUser(userId)\n * })\n *\n * const postsQueryOptions = () => queryOptions({\n * queryKey: ['posts'],\n * queryFn: () => fetchPosts()\n * })\n *\n * export default function Page({ userId }: { userId: string }) {\n * return (\n * <>\n * <Suspense fallback={<div>Loading user...</div>}>\n * <QueriesHydration queries={[userQueryOptions(userId)]}>\n * <UserProfile />\n * </QueriesHydration>\n * </Suspense>\n *\n * <Suspense fallback={<div>Loading posts...</div>}>\n * <QueriesHydration queries={[postsQueryOptions()]}>\n * <PostsList />\n * </QueriesHydration>\n * </Suspense>\n * </>\n * )\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With custom error fallback\n * <Suspense fallback={<div>Loading user...</div>}>\n * <QueriesHydration\n * queries={[userQueryOptions(userId)]}\n * skipSsrOnError={{ fallback: <div>Fetching on client...</div> }}\n * >\n * <UserProfile />\n * </QueriesHydration>\n * </Suspense>\n * ```\n *\n * @see {@link https://suspensive.org/docs/react-query/QueriesHydration Documentation}\n */\nexport async function QueriesHydration({\n queries,\n children,\n queryClient = new QueryClient(),\n skipSsrOnError = true,\n timeout,\n ...props\n}: {\n /**\n * The QueryClient instance to use for fetching queries.\n */\n queryClient?: QueryClient\n /**\n * An array of query options or infinite query options to be fetched on the server. Each query must include a `queryKey`.\n * You can mix regular queries and infinite queries in the same array.\n */\n queries: (\n | WithRequired<QueryOptions<any, any, any, any>, 'queryKey'>\n | WithRequired<UseInfiniteQueryOptions<any, any, any, any, any>, 'queryKey'>\n )[]\n /**\n * Controls error handling behavior:\n * - `true` (default): Skips SSR and falls back to client-side rendering when server fetch fails\n * - `false`: Proceeds with SSR without hydration (retry fetching on client component server rendering)\n * - `{ fallback: ReactNode }`: Skips SSR with custom fallback UI during client-side rendering\n */\n skipSsrOnError?:\n | boolean\n | {\n fallback: ReactNode\n }\n /**\n * The timeout in milliseconds for the query.\n * If the query takes longer than the timeout, it will be considered as an error.\n * When not set, no timeout is applied.\n */\n timeout?: number\n} & OmitKeyof<HydrateProps, 'state'>) {\n const timeoutController = timeout != null && timeout >= 0 ? createTimeoutController(timeout) : undefined\n try {\n const queriesPromise = Promise.all(\n queries.map((query) =>\n 'getNextPageParam' in query ? queryClient.fetchInfiniteQuery(query) : queryClient.fetchQuery(query)\n )\n )\n await (timeoutController != null ? Promise.race([queriesPromise, timeoutController.promise]) : queriesPromise)\n timeoutController?.clear()\n } catch {\n timeoutController?.clear()\n queries.forEach((query) => void queryClient.cancelQueries(query))\n if (skipSsrOnError) {\n return (\n <ClientOnly fallback={skipSsrOnError === true ? undefined : skipSsrOnError.fallback}>{children}</ClientOnly>\n )\n }\n }\n return (\n <Hydrate {...props} state={dehydrate(queryClient)}>\n {children}\n </Hydrate>\n )\n}\n\nconst createTimeoutController = (ms: number) => {\n let timerId: ReturnType<typeof setTimeout> | undefined\n return {\n promise: new Promise<never>((_, reject) => {\n timerId = setTimeout(() => reject(new Error(`QueriesHydration: timeout after ${ms} ms`)), ms)\n }),\n clear: () => timerId != null && clearTimeout(timerId),\n }\n}\n"],"mappings":";;;;;;;;;CA8EE;CACA;CACA;CACA;CACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AALF,SAAsB,iBAAiB;;;;wDAqCD;MArCC,EACrC,SACA,UACA,cAAc,IAAI,aAAa,EAC/B,iBAAiB,MACjB,kBACG;EAgCH,MAAM,oBAAoB,WAAW,QAAQ,WAAW,IAAI,wBAAwB,QAAQ,GAAG;AAC/F,MAAI;GACF,MAAM,iBAAiB,QAAQ,IAC7B,QAAQ,KAAK,UACX,sBAAsB,QAAQ,YAAY,mBAAmB,MAAM,GAAG,YAAY,WAAW,MAAM,CACpG,CACF;AACD,SAAO,qBAAqB,OAAO,QAAQ,KAAK,CAAC,gBAAgB,kBAAkB,QAAQ,CAAC,GAAG;AAC/F,mFAAmB,OAAO;oBACpB;AACN,mFAAmB,OAAO;AAC1B,WAAQ,SAAS,UAAU,KAAK,YAAY,cAAc,MAAM,CAAC;AACjE,OAAI,eACF,QACE,oBAAC,YAAD;IAAY,UAAU,mBAAmB,OAAO,SAAY,eAAe;IAAW;IAAsB;;AAIlH,SACE,oBAAC,2CAAY;GAAO,OAAO,UAAU,YAAY;GAC9C;IACO;;;;AAId,MAAM,2BAA2B,OAAe;CAC9C,IAAI;AACJ,QAAO;EACL,SAAS,IAAI,SAAgB,GAAG,WAAW;AACzC,aAAU,iBAAiB,uBAAO,IAAI,MAAM,mCAAmC,GAAG,KAAK,CAAC,EAAE,GAAG;IAC7F;EACF,aAAa,WAAW,QAAQ,aAAa,QAAQ;EACtD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suspensive/react-query-4",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.20.1",
|
|
4
4
|
"description": "Suspensive interfaces for @tanstack/react-query@4",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"suspensive",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"@types/react": "^19.2.14",
|
|
44
44
|
"react": "^19.2.4",
|
|
45
45
|
"@suspensive/eslint-config": "0.0.1",
|
|
46
|
-
"@suspensive/
|
|
47
|
-
"@suspensive/
|
|
46
|
+
"@suspensive/tsdown": "0.0.0",
|
|
47
|
+
"@suspensive/tsconfig": "0.0.0-development"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@tanstack/react-query": "*",
|
|
@@ -388,4 +388,156 @@ describe('<QueriesHydration/>', () => {
|
|
|
388
388
|
expect(screen.getByTestId('client-only')).toBeInTheDocument()
|
|
389
389
|
expect(screen.getByText('Client Child')).toBeInTheDocument()
|
|
390
390
|
})
|
|
391
|
+
|
|
392
|
+
it('should cancel queries when timeout occurs', async () => {
|
|
393
|
+
const queryClient = new QueryClient()
|
|
394
|
+
const cancelQueriesSpy = vi.spyOn(queryClient, 'cancelQueries')
|
|
395
|
+
const timeoutMs = 100
|
|
396
|
+
const queryDelayMs = 200
|
|
397
|
+
|
|
398
|
+
const queries = [
|
|
399
|
+
{
|
|
400
|
+
queryKey: ['slow-query-1'],
|
|
401
|
+
queryFn: () => new Promise((resolve) => setTimeout(() => resolve({ data: '1' }), queryDelayMs)),
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
queryKey: ['slow-query-2'],
|
|
405
|
+
queryFn: () => new Promise((resolve) => setTimeout(() => resolve({ data: '2' }), queryDelayMs)),
|
|
406
|
+
},
|
|
407
|
+
]
|
|
408
|
+
|
|
409
|
+
await QueriesHydration({
|
|
410
|
+
queries,
|
|
411
|
+
queryClient,
|
|
412
|
+
timeout: timeoutMs,
|
|
413
|
+
children: <div>children</div>,
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
expect(cancelQueriesSpy).toHaveBeenCalledTimes(2)
|
|
417
|
+
expect(cancelQueriesSpy).toHaveBeenCalledWith(queries[0])
|
|
418
|
+
expect(cancelQueriesSpy).toHaveBeenCalledWith(queries[1])
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
it('should cancel queries when query fails without timeout', async () => {
|
|
422
|
+
const queryClient = new QueryClient()
|
|
423
|
+
const cancelQueriesSpy = vi.spyOn(queryClient, 'cancelQueries')
|
|
424
|
+
|
|
425
|
+
const queries = [
|
|
426
|
+
{
|
|
427
|
+
queryKey: ['failing-query'],
|
|
428
|
+
queryFn: () => Promise.reject(new Error('network error')),
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
queryKey: ['slow-query'],
|
|
432
|
+
queryFn: () => new Promise((resolve) => setTimeout(() => resolve({ data: 'slow' }), 200)),
|
|
433
|
+
},
|
|
434
|
+
]
|
|
435
|
+
|
|
436
|
+
await QueriesHydration({
|
|
437
|
+
queries,
|
|
438
|
+
queryClient,
|
|
439
|
+
children: <div>children</div>,
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
expect(cancelQueriesSpy).toHaveBeenCalledTimes(2)
|
|
443
|
+
expect(cancelQueriesSpy).toHaveBeenCalledWith(queries[0])
|
|
444
|
+
expect(cancelQueriesSpy).toHaveBeenCalledWith(queries[1])
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
it('should not cancel queries on successful fetch', async () => {
|
|
448
|
+
const queryClient = new QueryClient()
|
|
449
|
+
const cancelQueriesSpy = vi.spyOn(queryClient, 'cancelQueries')
|
|
450
|
+
|
|
451
|
+
const queries = [
|
|
452
|
+
{
|
|
453
|
+
queryKey: ['success-query'],
|
|
454
|
+
queryFn: () => Promise.resolve({ data: 'ok' }),
|
|
455
|
+
},
|
|
456
|
+
]
|
|
457
|
+
|
|
458
|
+
await QueriesHydration({
|
|
459
|
+
queries,
|
|
460
|
+
queryClient,
|
|
461
|
+
timeout: 5000,
|
|
462
|
+
children: <div>children</div>,
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
expect(cancelQueriesSpy).not.toHaveBeenCalled()
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
it('should cancel all queries even when only some are slow enough to timeout', async () => {
|
|
469
|
+
const queryClient = new QueryClient()
|
|
470
|
+
const cancelQueriesSpy = vi.spyOn(queryClient, 'cancelQueries')
|
|
471
|
+
const timeoutMs = 100
|
|
472
|
+
|
|
473
|
+
const queries = [
|
|
474
|
+
{
|
|
475
|
+
queryKey: ['fast-query'],
|
|
476
|
+
queryFn: () => Promise.resolve({ data: 'fast' }),
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
queryKey: ['slow-query'],
|
|
480
|
+
queryFn: () => new Promise((resolve) => setTimeout(() => resolve({ data: 'slow' }), 200)),
|
|
481
|
+
},
|
|
482
|
+
]
|
|
483
|
+
|
|
484
|
+
await QueriesHydration({
|
|
485
|
+
queries,
|
|
486
|
+
queryClient,
|
|
487
|
+
timeout: timeoutMs,
|
|
488
|
+
children: <div>children</div>,
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
// Promise.all waits for all, so timeout cancels all queries in the list
|
|
492
|
+
expect(cancelQueriesSpy).toHaveBeenCalledTimes(2)
|
|
493
|
+
expect(cancelQueriesSpy).toHaveBeenCalledWith(queries[0])
|
|
494
|
+
expect(cancelQueriesSpy).toHaveBeenCalledWith(queries[1])
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
it('should still skip SSR on timeout when skipSsrOnError is true', async () => {
|
|
498
|
+
const queryClient = new QueryClient()
|
|
499
|
+
|
|
500
|
+
const queries = [
|
|
501
|
+
{
|
|
502
|
+
queryKey: ['slow-query'],
|
|
503
|
+
queryFn: () => new Promise((resolve) => setTimeout(() => resolve({ data: 'slow' }), 200)),
|
|
504
|
+
},
|
|
505
|
+
]
|
|
506
|
+
|
|
507
|
+
const result = await QueriesHydration({
|
|
508
|
+
queries,
|
|
509
|
+
queryClient,
|
|
510
|
+
timeout: 100,
|
|
511
|
+
skipSsrOnError: true,
|
|
512
|
+
children: <div>children</div>,
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
render(result as React.ReactElement)
|
|
516
|
+
expect(screen.getByTestId('client-only')).toBeInTheDocument()
|
|
517
|
+
})
|
|
518
|
+
|
|
519
|
+
it('should cancel queries on timeout but proceed with SSR when skipSsrOnError is false', async () => {
|
|
520
|
+
const queryClient = new QueryClient()
|
|
521
|
+
const cancelQueriesSpy = vi.spyOn(queryClient, 'cancelQueries')
|
|
522
|
+
|
|
523
|
+
const queries = [
|
|
524
|
+
{
|
|
525
|
+
queryKey: ['slow-query'],
|
|
526
|
+
queryFn: () => new Promise((resolve) => setTimeout(() => resolve({ data: 'slow' }), 200)),
|
|
527
|
+
},
|
|
528
|
+
]
|
|
529
|
+
|
|
530
|
+
const result = await QueriesHydration({
|
|
531
|
+
queries,
|
|
532
|
+
queryClient,
|
|
533
|
+
timeout: 100,
|
|
534
|
+
skipSsrOnError: false,
|
|
535
|
+
children: <div>children</div>,
|
|
536
|
+
})
|
|
537
|
+
|
|
538
|
+
expect(cancelQueriesSpy).toHaveBeenCalledTimes(1)
|
|
539
|
+
expect(cancelQueriesSpy).toHaveBeenCalledWith(queries[0])
|
|
540
|
+
// skipSsrOnError is false, so it should render Hydrate, not ClientOnly
|
|
541
|
+
expect(result.type).not.toEqual(expect.objectContaining({ name: 'ClientOnly' }))
|
|
542
|
+
})
|
|
391
543
|
})
|
package/src/QueriesHydration.tsx
CHANGED
|
@@ -113,10 +113,7 @@ export async function QueriesHydration({
|
|
|
113
113
|
*/
|
|
114
114
|
timeout?: number
|
|
115
115
|
} & OmitKeyof<HydrateProps, 'state'>) {
|
|
116
|
-
const timeoutController =
|
|
117
|
-
timeout != null && timeout >= 0
|
|
118
|
-
? createTimeoutController(timeout, `QueriesHydration: timeout after ${timeout} ms)`)
|
|
119
|
-
: undefined
|
|
116
|
+
const timeoutController = timeout != null && timeout >= 0 ? createTimeoutController(timeout) : undefined
|
|
120
117
|
try {
|
|
121
118
|
const queriesPromise = Promise.all(
|
|
122
119
|
queries.map((query) =>
|
|
@@ -127,6 +124,7 @@ export async function QueriesHydration({
|
|
|
127
124
|
timeoutController?.clear()
|
|
128
125
|
} catch {
|
|
129
126
|
timeoutController?.clear()
|
|
127
|
+
queries.forEach((query) => void queryClient.cancelQueries(query))
|
|
130
128
|
if (skipSsrOnError) {
|
|
131
129
|
return (
|
|
132
130
|
<ClientOnly fallback={skipSsrOnError === true ? undefined : skipSsrOnError.fallback}>{children}</ClientOnly>
|
|
@@ -140,11 +138,11 @@ export async function QueriesHydration({
|
|
|
140
138
|
)
|
|
141
139
|
}
|
|
142
140
|
|
|
143
|
-
const createTimeoutController = (ms: number
|
|
141
|
+
const createTimeoutController = (ms: number) => {
|
|
144
142
|
let timerId: ReturnType<typeof setTimeout> | undefined
|
|
145
143
|
return {
|
|
146
144
|
promise: new Promise<never>((_, reject) => {
|
|
147
|
-
timerId = setTimeout(() => reject(new Error(
|
|
145
|
+
timerId = setTimeout(() => reject(new Error(`QueriesHydration: timeout after ${ms} ms`)), ms)
|
|
148
146
|
}),
|
|
149
147
|
clear: () => timerId != null && clearTimeout(timerId),
|
|
150
148
|
}
|