@raf-query/react 0.11.1 → 0.12.0
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.
- package/CHANGELOG.md +11 -1
- package/README.md +32 -59
- package/dist/bind.d.mts +19 -0
- package/dist/bind.mjs +21 -0
- package/dist/browser-render.mjs +46 -0
- package/dist/devtools.d.mts +24 -2
- package/dist/devtools.mjs +236 -1
- package/dist/hydration-boundary/hydrate.mjs +10 -13
- package/dist/hydration-boundary/hydration-boundary-react-server.d.mts +4 -0
- package/dist/hydration-boundary/hydration-boundary-react-server.mjs +18 -9
- package/dist/hydration-boundary/hydration-boundary.d.mts +9 -29
- package/dist/hydration-boundary/hydration-boundary.mjs +6 -27
- package/dist/index-react-server.d.mts +7 -6
- package/dist/index-react-server.mjs +4 -6
- package/dist/index-shared.d.mts +5 -6
- package/dist/index-shared.mjs +3 -6
- package/dist/index.d.mts +7 -6
- package/dist/index.mjs +5 -4
- package/dist/provider.d.mts +4 -4
- package/dist/provider.mjs +5 -8
- package/dist/react-server-hooks.d.mts +17 -0
- package/dist/react-server-hooks.mjs +19 -0
- package/dist/tanstack-router-shared.d.mts +4 -7
- package/dist/tanstack-router-shared.mjs +12 -22
- package/dist/tanstack-router.d.mts +2 -2
- package/dist/tanstack-router.mjs +10 -14
- package/dist/tracked.d.mts +1 -0
- package/dist/tracked.mjs +65 -0
- package/dist/use-infinite-query.d.mts +27 -0
- package/dist/use-infinite-query.mjs +35 -0
- package/dist/use-is-fetching.d.mts +6 -16
- package/dist/use-is-fetching.mjs +11 -31
- package/dist/use-query.d.mts +43 -54
- package/dist/use-query.mjs +102 -47
- package/package.json +10 -9
- package/dist/devtools-panel.d.mts +0 -38
- package/dist/devtools-panel.mjs +0 -896
- package/dist/devtools-store.mjs +0 -104
- package/dist/hydration-boundary/sent.mjs +0 -16
- package/dist/query-provider/browser-requests.mjs +0 -38
- package/dist/query-provider/query-provider.d.mts +0 -21
- package/dist/query-provider/query-provider.mjs +0 -33
- package/dist/use-waterfall.d.mts +0 -18
- package/dist/use-waterfall.mjs +0 -41
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DehydrateOptions, DehydratedState,
|
|
1
|
+
import { DehydrateOptions, DehydratedState, QueryClient } from "@raf-query/core";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
//#region src/hydration-boundary/hydration-boundary.d.ts
|
|
4
4
|
export type HydrationBoundaryProps = {
|
|
@@ -13,8 +13,8 @@ export type HydrationBoundaryProps = {
|
|
|
13
13
|
} | {
|
|
14
14
|
readonly children?: ReactNode;
|
|
15
15
|
readonly client?: never;
|
|
16
|
-
readonly options?:
|
|
17
|
-
/** A loader's `dehydrate(
|
|
16
|
+
readonly options?: never;
|
|
17
|
+
/** A loader's `dehydrate(client)`. */
|
|
18
18
|
readonly state: DehydratedState;
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
@@ -27,38 +27,18 @@ export type HydrationBoundaryProps = {
|
|
|
27
27
|
* through the package's `react-server` export condition.
|
|
28
28
|
*
|
|
29
29
|
* ```tsx
|
|
30
|
-
* const
|
|
31
|
-
* const
|
|
30
|
+
* const client = getQueryClient();
|
|
31
|
+
* const [handles] = await load(client, { comments: prefetch(commentsQuery, { slug }) });
|
|
32
32
|
* return (
|
|
33
|
-
* <HydrationBoundary client={
|
|
34
|
-
* <Comments handle={comments} />
|
|
33
|
+
* <HydrationBoundary client={client}>
|
|
34
|
+
* <Comments handle={handles.comments} />
|
|
35
35
|
* </HydrationBoundary>
|
|
36
36
|
* );
|
|
37
37
|
* ```
|
|
38
38
|
*
|
|
39
39
|
* With loaders, such as React Router Framework Mode, pass the `state` the
|
|
40
|
-
* loader dehydrated
|
|
41
|
-
*
|
|
42
|
-
* ```tsx
|
|
43
|
-
* export async function loader(args: Route.LoaderArgs) {
|
|
44
|
-
* const qc = new QueryClient();
|
|
45
|
-
* const post = await qc.ensure(postQuery, { slug: args.params.slug });
|
|
46
|
-
* return { dehydratedState: dehydrate(qc), post };
|
|
47
|
-
* }
|
|
48
|
-
*
|
|
49
|
-
* export default function Page(props: Route.ComponentProps) {
|
|
50
|
-
* const { dehydratedState, post } = props.loaderData;
|
|
51
|
-
* return (
|
|
52
|
-
* <HydrationBoundary state={dehydratedState}>
|
|
53
|
-
* <Post handle={post} />
|
|
54
|
-
* </HydrationBoundary>
|
|
55
|
-
* );
|
|
56
|
-
* }
|
|
57
|
-
* ```
|
|
58
|
-
*
|
|
59
|
-
* Pending entries cross as promises when the framework's serializer
|
|
60
|
-
* supports them (React Router's Single Fetch does). Nested boundaries
|
|
61
|
-
* are fine: each only writes what its `state` holds.
|
|
40
|
+
* loader dehydrated. Pending entries cross as promises when the framework's
|
|
41
|
+
* serializer supports them (React Router's Single Fetch does).
|
|
62
42
|
*/
|
|
63
43
|
export declare function HydrationBoundary(props: HydrationBoundaryProps): import("react").JSX.Element;
|
|
64
44
|
//#endregion
|
|
@@ -12,43 +12,22 @@ import { jsx } from "react/jsx-runtime";
|
|
|
12
12
|
* through the package's `react-server` export condition.
|
|
13
13
|
*
|
|
14
14
|
* ```tsx
|
|
15
|
-
* const
|
|
16
|
-
* const
|
|
15
|
+
* const client = getQueryClient();
|
|
16
|
+
* const [handles] = await load(client, { comments: prefetch(commentsQuery, { slug }) });
|
|
17
17
|
* return (
|
|
18
|
-
* <HydrationBoundary client={
|
|
19
|
-
* <Comments handle={comments} />
|
|
18
|
+
* <HydrationBoundary client={client}>
|
|
19
|
+
* <Comments handle={handles.comments} />
|
|
20
20
|
* </HydrationBoundary>
|
|
21
21
|
* );
|
|
22
22
|
* ```
|
|
23
23
|
*
|
|
24
24
|
* With loaders, such as React Router Framework Mode, pass the `state` the
|
|
25
|
-
* loader dehydrated
|
|
26
|
-
*
|
|
27
|
-
* ```tsx
|
|
28
|
-
* export async function loader(args: Route.LoaderArgs) {
|
|
29
|
-
* const qc = new QueryClient();
|
|
30
|
-
* const post = await qc.ensure(postQuery, { slug: args.params.slug });
|
|
31
|
-
* return { dehydratedState: dehydrate(qc), post };
|
|
32
|
-
* }
|
|
33
|
-
*
|
|
34
|
-
* export default function Page(props: Route.ComponentProps) {
|
|
35
|
-
* const { dehydratedState, post } = props.loaderData;
|
|
36
|
-
* return (
|
|
37
|
-
* <HydrationBoundary state={dehydratedState}>
|
|
38
|
-
* <Post handle={post} />
|
|
39
|
-
* </HydrationBoundary>
|
|
40
|
-
* );
|
|
41
|
-
* }
|
|
42
|
-
* ```
|
|
43
|
-
*
|
|
44
|
-
* Pending entries cross as promises when the framework's serializer
|
|
45
|
-
* supports them (React Router's Single Fetch does). Nested boundaries
|
|
46
|
-
* are fine: each only writes what its `state` holds.
|
|
25
|
+
* loader dehydrated. Pending entries cross as promises when the framework's
|
|
26
|
+
* serializer supports them (React Router's Single Fetch does).
|
|
47
27
|
*/
|
|
48
28
|
function HydrationBoundary(props) {
|
|
49
29
|
if (props.state === void 0) throw new Error("HydrationBoundary needs `state` here. `client` works only in server components.");
|
|
50
30
|
return /* @__PURE__ */ jsx(Hydrate, {
|
|
51
|
-
options: props.options,
|
|
52
31
|
state: props.state,
|
|
53
32
|
children: props.children
|
|
54
33
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { useWaterfall } from "./use-waterfall.mjs";
|
|
1
|
+
import { QueryDevtools, QueryDevtoolsPanel, QueryDevtoolsPanelProps, QueryDevtoolsProps } from "./devtools.mjs";
|
|
2
|
+
import { QueryClientProvider } from "./provider.mjs";
|
|
3
|
+
import { QueryState, QueryValue, UseQueryOptions } from "./use-query.mjs";
|
|
4
|
+
import { InfiniteQueryState, PageControls } from "./use-infinite-query.mjs";
|
|
6
5
|
import "./index-shared.mjs";
|
|
6
|
+
import { HydrationBoundaryProps } from "./hydration-boundary/hydration-boundary.mjs";
|
|
7
7
|
import { HydrationBoundary } from "./hydration-boundary/hydration-boundary-react-server.mjs";
|
|
8
|
+
import { bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient } from "./react-server-hooks.mjs";
|
|
8
9
|
export * from "@raf-query/core";
|
|
9
|
-
export { HydrationBoundary, QueryClientProvider,
|
|
10
|
+
export { HydrationBoundary, type HydrationBoundaryProps, type InfiniteQueryState, type PageControls, QueryClientProvider, QueryDevtools, QueryDevtoolsPanel, type QueryDevtoolsPanelProps, type QueryDevtoolsProps, type QueryState, type QueryValue, type UseQueryOptions, bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient };
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { QueryClientProvider
|
|
2
|
-
import {
|
|
3
|
-
import { useIsFetching } from "./use-is-fetching.mjs";
|
|
4
|
-
import { useQuery } from "./use-query.mjs";
|
|
5
|
-
import { useWaterfall } from "./use-waterfall.mjs";
|
|
1
|
+
import { QueryClientProvider } from "./provider.mjs";
|
|
2
|
+
import { QueryDevtools, QueryDevtoolsPanel } from "./devtools.mjs";
|
|
6
3
|
import { HydrationBoundary } from "./hydration-boundary/hydration-boundary-react-server.mjs";
|
|
4
|
+
import { bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient } from "./react-server-hooks.mjs";
|
|
7
5
|
export * from "@raf-query/core";
|
|
8
|
-
export { HydrationBoundary, QueryClientProvider,
|
|
6
|
+
export { HydrationBoundary, QueryClientProvider, QueryDevtools, QueryDevtoolsPanel, bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient };
|
package/dist/index-shared.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { useWaterfall } from "./use-waterfall.mjs";
|
|
1
|
+
import { QueryDevtools, QueryDevtoolsPanel, QueryDevtoolsPanelProps, QueryDevtoolsProps } from "./devtools.mjs";
|
|
2
|
+
import { QueryClientProvider } from "./provider.mjs";
|
|
3
|
+
import { QueryState, QueryValue, UseQueryOptions } from "./use-query.mjs";
|
|
4
|
+
import { InfiniteQueryState, PageControls } from "./use-infinite-query.mjs";
|
|
6
5
|
export * from "@raf-query/core";
|
|
7
|
-
export { QueryClientProvider,
|
|
6
|
+
export { type InfiniteQueryState, type PageControls, QueryClientProvider, QueryDevtools, QueryDevtoolsPanel, type QueryDevtoolsPanelProps, type QueryDevtoolsProps, type QueryState, type QueryValue, type UseQueryOptions };
|
package/dist/index-shared.mjs
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import { QueryClientProvider
|
|
2
|
-
import {
|
|
3
|
-
import { useIsFetching } from "./use-is-fetching.mjs";
|
|
4
|
-
import { useQuery } from "./use-query.mjs";
|
|
5
|
-
import { useWaterfall } from "./use-waterfall.mjs";
|
|
1
|
+
import { QueryClientProvider } from "./provider.mjs";
|
|
2
|
+
import { QueryDevtools, QueryDevtoolsPanel } from "./devtools.mjs";
|
|
6
3
|
export * from "@raf-query/core";
|
|
7
|
-
export { QueryClientProvider,
|
|
4
|
+
export { QueryClientProvider, QueryDevtools, QueryDevtoolsPanel };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { QueryDevtools, QueryDevtoolsPanel, QueryDevtoolsPanelProps, QueryDevtoolsProps } from "./devtools.mjs";
|
|
1
2
|
import { QueryClientProvider, useQueryClient } from "./provider.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { UseQueryResult, UseQueryResultWithData, useQuery } from "./use-query.mjs";
|
|
5
|
-
import { useWaterfall } from "./use-waterfall.mjs";
|
|
3
|
+
import { QueryState, QueryValue, UseQueryOptions, useQuery } from "./use-query.mjs";
|
|
4
|
+
import { InfiniteQueryState, PageControls, useInfiniteQuery } from "./use-infinite-query.mjs";
|
|
6
5
|
import "./index-shared.mjs";
|
|
7
|
-
import { HydrationBoundary } from "./hydration-boundary/hydration-boundary.mjs";
|
|
6
|
+
import { HydrationBoundary, HydrationBoundaryProps } from "./hydration-boundary/hydration-boundary.mjs";
|
|
7
|
+
import { useIsFetching } from "./use-is-fetching.mjs";
|
|
8
|
+
import { bindUseQueryClient } from "./bind.mjs";
|
|
8
9
|
export * from "@raf-query/core";
|
|
9
|
-
export { HydrationBoundary, QueryClientProvider,
|
|
10
|
+
export { HydrationBoundary, type HydrationBoundaryProps, type InfiniteQueryState, type PageControls, QueryClientProvider, QueryDevtools, QueryDevtoolsPanel, type QueryDevtoolsPanelProps, type QueryDevtoolsProps, type QueryState, type QueryValue, type UseQueryOptions, bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient };
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { QueryClientProvider, useQueryClient } from "./provider.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { QueryDevtools, QueryDevtoolsPanel } from "./devtools.mjs";
|
|
3
|
+
import { bindUseQueryClient } from "./bind.mjs";
|
|
4
|
+
import { HydrationBoundary } from "./hydration-boundary/hydration-boundary.mjs";
|
|
3
5
|
import { useIsFetching } from "./use-is-fetching.mjs";
|
|
4
6
|
import { useQuery } from "./use-query.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import { HydrationBoundary } from "./hydration-boundary/hydration-boundary.mjs";
|
|
7
|
+
import { useInfiniteQuery } from "./use-infinite-query.mjs";
|
|
7
8
|
export * from "@raf-query/core";
|
|
8
|
-
export { HydrationBoundary, QueryClientProvider,
|
|
9
|
+
export { HydrationBoundary, QueryClientProvider, QueryDevtools, QueryDevtoolsPanel, bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient };
|
package/dist/provider.d.mts
CHANGED
|
@@ -2,13 +2,13 @@ import { QueryClient } from "@raf-query/core";
|
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
//#region src/provider.d.ts
|
|
4
4
|
/**
|
|
5
|
-
* Provides a client to the tree, and
|
|
6
|
-
*
|
|
5
|
+
* Provides a client to the tree, and runs its features (focus and
|
|
6
|
+
* reconnect refetching, polling, …) while mounted.
|
|
7
7
|
*/
|
|
8
8
|
export declare function QueryClientProvider(props: {
|
|
9
9
|
readonly children?: ReactNode;
|
|
10
|
-
readonly client: QueryClient
|
|
10
|
+
readonly client: QueryClient;
|
|
11
11
|
}): import("react").JSX.Element;
|
|
12
12
|
/** The client from the nearest {@link QueryClientProvider}. */
|
|
13
|
-
export declare function useQueryClient(): QueryClient
|
|
13
|
+
export declare function useQueryClient(): QueryClient;
|
|
14
14
|
//#endregion
|
package/dist/provider.mjs
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { mount } from "@raf-query/core/internal";
|
|
2
3
|
import { createContext, use, useEffect } from "react";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
5
|
//#region src/provider.tsx
|
|
5
|
-
/**
|
|
6
|
-
* A hook can't know which services the provider's client has, so its verbs
|
|
7
|
-
* take any factory here.
|
|
8
|
-
*/
|
|
9
6
|
const QueryClientContext = createContext(null);
|
|
10
7
|
/**
|
|
11
|
-
* Provides a client to the tree, and
|
|
12
|
-
*
|
|
8
|
+
* Provides a client to the tree, and runs its features (focus and
|
|
9
|
+
* reconnect refetching, polling, …) while mounted.
|
|
13
10
|
*/
|
|
14
11
|
function QueryClientProvider(props) {
|
|
15
12
|
const client = props.client;
|
|
16
|
-
useEffect(() =>
|
|
13
|
+
useEffect(() => mount(client), [client]);
|
|
17
14
|
return /* @__PURE__ */ jsx(QueryClientContext, {
|
|
18
15
|
value: client,
|
|
19
16
|
children: props.children
|
|
@@ -25,7 +22,7 @@ function useOptionalQueryClient() {
|
|
|
25
22
|
}
|
|
26
23
|
/** The client from the nearest {@link QueryClientProvider}. */
|
|
27
24
|
function useQueryClient() {
|
|
28
|
-
const client =
|
|
25
|
+
const client = useOptionalQueryClient();
|
|
29
26
|
if (!client) throw new Error("useQueryClient: no QueryClientProvider above this component");
|
|
30
27
|
return client;
|
|
31
28
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useQueryClient as useQueryClient$1 } from "./provider.mjs";
|
|
2
|
+
import { useQuery as useQuery$1 } from "./use-query.mjs";
|
|
3
|
+
import { useInfiniteQuery as useInfiniteQuery$1 } from "./use-infinite-query.mjs";
|
|
4
|
+
import { useIsFetching as useIsFetching$1 } from "./use-is-fetching.mjs";
|
|
5
|
+
import { QueryClient } from "@raf-query/core";
|
|
6
|
+
import { RootOf } from "@raf-query/core/internal";
|
|
7
|
+
//#region src/react-server-hooks.d.ts
|
|
8
|
+
export declare const useQuery: typeof useQuery$1;
|
|
9
|
+
export declare const useInfiniteQuery: typeof useInfiniteQuery$1;
|
|
10
|
+
export declare const useIsFetching: typeof useIsFetching$1;
|
|
11
|
+
export declare const useQueryClient: typeof useQueryClient$1;
|
|
12
|
+
/**
|
|
13
|
+
* `bindUseQueryClient` runs at module level in a file server modules may
|
|
14
|
+
* import too, so it works here and returns the throwing `useQueryClient`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function bindUseQueryClient<T>(): () => QueryClient<RootOf<T>>;
|
|
17
|
+
//#endregion
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/react-server-hooks.ts
|
|
2
|
+
function clientOnly(name) {
|
|
3
|
+
return () => {
|
|
4
|
+
throw new Error(`${name} reads in client components: move this into a file with "use client". Server components start queries with load() and send them with <HydrationBoundary>.`);
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
const useQuery = /* @__PURE__ */ clientOnly("useQuery");
|
|
8
|
+
const useInfiniteQuery = /* @__PURE__ */ clientOnly("useInfiniteQuery");
|
|
9
|
+
const useIsFetching = /* @__PURE__ */ clientOnly("useIsFetching");
|
|
10
|
+
const useQueryClient = /* @__PURE__ */ clientOnly("useQueryClient");
|
|
11
|
+
/**
|
|
12
|
+
* `bindUseQueryClient` runs at module level in a file server modules may
|
|
13
|
+
* import too, so it works here and returns the throwing `useQueryClient`.
|
|
14
|
+
*/
|
|
15
|
+
function bindUseQueryClient() {
|
|
16
|
+
return useQueryClient;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient };
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import { DehydrateOptions, DehydratedState,
|
|
1
|
+
import { DehydrateOptions, DehydratedState, QueryClient, QueryHandle } from "@raf-query/core";
|
|
2
2
|
import { AnyRouter } from "@tanstack/react-router";
|
|
3
3
|
//#region src/tanstack-router-shared.d.ts
|
|
4
4
|
declare module "@tanstack/react-router" {
|
|
5
5
|
interface SerializableExtensions {
|
|
6
6
|
/**
|
|
7
|
-
* Handles are `{
|
|
8
|
-
* sends as is
|
|
9
|
-
* type
|
|
10
|
-
* `ReadonlyArray<unknown>`, and the handle type names its factory.
|
|
11
|
-
* So `beforeLoad` can put handles in route context.
|
|
7
|
+
* Handles are plain `{ queryKey, input }`, which the router's serializer
|
|
8
|
+
* sends as is. Its type check can't see that on its own: the handle
|
|
9
|
+
* type names its factory. So loaders can return handles.
|
|
12
10
|
*/
|
|
13
11
|
rafQueryHandle: QueryHandle;
|
|
14
12
|
}
|
|
@@ -17,7 +15,6 @@ export interface RouterIntegrationOptions<TRouter extends AnyRouter> {
|
|
|
17
15
|
readonly dehydrateOptions?: DehydrateOptions;
|
|
18
16
|
/** Navigate when a `queryFn` throws `redirect()`. Default `true`. */
|
|
19
17
|
readonly handleRedirects?: boolean;
|
|
20
|
-
readonly hydrateOptions?: HydrateOptions;
|
|
21
18
|
readonly queryClient: QueryClient;
|
|
22
19
|
readonly router: TRouter;
|
|
23
20
|
}
|
|
@@ -1,34 +1,22 @@
|
|
|
1
1
|
import { hydrate } from "@raf-query/core";
|
|
2
|
-
import { Predicate } from "effect";
|
|
3
2
|
import { isRedirect } from "@tanstack/react-router";
|
|
4
3
|
//#region src/tanstack-router-shared.ts
|
|
5
4
|
function hasRafQueries(value) {
|
|
6
|
-
return
|
|
5
|
+
return typeof value === "object" && value !== null && "raf" in value;
|
|
7
6
|
}
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* put Effect's `Stream` and `Queue` in the browser bundle.
|
|
12
|
-
*/
|
|
13
|
-
async function hydrateEach(client, stream, options) {
|
|
14
|
-
const reader = stream.getReader();
|
|
15
|
-
for (;;) {
|
|
16
|
-
const next = await reader.read();
|
|
17
|
-
if (next.done) return;
|
|
18
|
-
hydrate(client, next.value, options);
|
|
19
|
-
}
|
|
7
|
+
/** Hydrate each state as the server's stream delivers it. */
|
|
8
|
+
async function hydrateEach(client, stream) {
|
|
9
|
+
for await (const state of stream) hydrate(client, state);
|
|
20
10
|
}
|
|
21
11
|
function setupClient(options) {
|
|
22
12
|
const router = options.router;
|
|
23
13
|
const client = options.queryClient;
|
|
24
|
-
const hydrateOptions = options.hydrateOptions;
|
|
25
14
|
const originalHydrate = router.options.hydrate;
|
|
26
15
|
router.options.hydrate = async (dehydrated) => {
|
|
27
16
|
await originalHydrate?.(dehydrated);
|
|
28
17
|
if (!hasRafQueries(dehydrated)) return;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
hydrateEach(client, raf.stream, hydrateOptions).catch((error) => {
|
|
18
|
+
hydrate(client, dehydrated.raf.initial);
|
|
19
|
+
hydrateEach(client, dehydrated.raf.stream).catch((error) => {
|
|
32
20
|
console.error("raf-query: reading the query stream failed", error);
|
|
33
21
|
});
|
|
34
22
|
};
|
|
@@ -36,11 +24,13 @@ function setupClient(options) {
|
|
|
36
24
|
function handleRedirects(options) {
|
|
37
25
|
if (!(options.handleRedirects ?? true)) return;
|
|
38
26
|
const router = options.router;
|
|
27
|
+
const handled = /* @__PURE__ */ new WeakSet();
|
|
39
28
|
options.queryClient.subscribe((event) => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
29
|
+
const error = event.entry.state.error;
|
|
30
|
+
if (event.type === "updated" && isRedirect(error) && !handled.has(error)) {
|
|
31
|
+
handled.add(error);
|
|
32
|
+
error.options._fromLocation = router.stores.location.get();
|
|
33
|
+
router.navigate(router.resolveRedirect(error).options);
|
|
44
34
|
}
|
|
45
35
|
});
|
|
46
36
|
}
|
|
@@ -5,8 +5,8 @@ import { AnyRouter } from "@tanstack/react-router";
|
|
|
5
5
|
* Connects a query client to a TanStack Router (and TanStack Start) router:
|
|
6
6
|
*
|
|
7
7
|
* - During SSR, sends the cache to the browser with the router's dehydrated
|
|
8
|
-
* state, then streams entries as they start
|
|
9
|
-
*
|
|
8
|
+
* state, then streams entries as they start. Pending entries cross as
|
|
9
|
+
* promises, so the browser resumes them instead of refetching.
|
|
10
10
|
* - Clears the request's cache when the response finishes.
|
|
11
11
|
* - Turns `redirect()` thrown from a `queryFn` into a navigation.
|
|
12
12
|
*
|
package/dist/tanstack-router.mjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { handleRedirects, setupClient } from "./tanstack-router-shared.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { dehydrateStream, removeAll } from "@raf-query/core";
|
|
3
3
|
//#region src/tanstack-router.ts
|
|
4
4
|
const noop = () => {};
|
|
5
5
|
function setupServer(options) {
|
|
6
6
|
const router = options.router;
|
|
7
|
-
const
|
|
7
|
+
const client = options.queryClient;
|
|
8
8
|
const originalDehydrate = router.options.dehydrate;
|
|
9
9
|
let finish = noop;
|
|
10
10
|
let cleanedUp = false;
|
|
11
11
|
const teardown = () => {
|
|
12
12
|
cleanedUp = true;
|
|
13
13
|
finish();
|
|
14
|
-
|
|
14
|
+
removeAll(client);
|
|
15
15
|
};
|
|
16
16
|
router.serverSsrLifecycle = {
|
|
17
17
|
...router.serverSsrLifecycle,
|
|
@@ -20,21 +20,17 @@ function setupServer(options) {
|
|
|
20
20
|
router.options.dehydrate = async () => {
|
|
21
21
|
const dehydrated = await originalDehydrate?.();
|
|
22
22
|
if (cleanedUp) {
|
|
23
|
-
|
|
23
|
+
removeAll(client);
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
finish = noop;
|
|
30
|
-
Effect.runSync(Scope.close(scope, Exit.void));
|
|
31
|
-
};
|
|
32
|
-
router.serverSsr?.onRenderFinished(() => finish());
|
|
26
|
+
const stream = dehydrateStream(client, options.dehydrateOptions);
|
|
27
|
+
finish = () => stream.close();
|
|
28
|
+
router.serverSsr?.onRenderFinished(() => stream.close());
|
|
33
29
|
return {
|
|
34
30
|
...dehydrated,
|
|
35
31
|
raf: {
|
|
36
32
|
initial: stream.initial,
|
|
37
|
-
stream:
|
|
33
|
+
stream: stream.updates
|
|
38
34
|
}
|
|
39
35
|
};
|
|
40
36
|
};
|
|
@@ -43,8 +39,8 @@ function setupServer(options) {
|
|
|
43
39
|
* Connects a query client to a TanStack Router (and TanStack Start) router:
|
|
44
40
|
*
|
|
45
41
|
* - During SSR, sends the cache to the browser with the router's dehydrated
|
|
46
|
-
* state, then streams entries as they start
|
|
47
|
-
*
|
|
42
|
+
* state, then streams entries as they start. Pending entries cross as
|
|
43
|
+
* promises, so the browser resumes them instead of refetching.
|
|
48
44
|
* - Clears the request's cache when the response finishes.
|
|
49
45
|
* - Turns `redirect()` thrown from a `queryFn` into a navigation.
|
|
50
46
|
*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { Thenable } from "@raf-query/core";
|
package/dist/tracked.mjs
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//#region src/tracked.ts
|
|
2
|
+
function createTracker() {
|
|
3
|
+
return {
|
|
4
|
+
actions: /* @__PURE__ */ new Map(),
|
|
5
|
+
proxy: void 0,
|
|
6
|
+
rendered: void 0,
|
|
7
|
+
tracked: /* @__PURE__ */ new Set()
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function renderedOf(tracker) {
|
|
11
|
+
return tracker.rendered;
|
|
12
|
+
}
|
|
13
|
+
/** A function that calls the latest render's, so it keeps its identity across renders. */
|
|
14
|
+
function stableAction(tracker, name) {
|
|
15
|
+
let action = tracker.actions.get(name);
|
|
16
|
+
if (!action) {
|
|
17
|
+
action = (...args) => {
|
|
18
|
+
const latest = renderedOf(tracker).actions[name];
|
|
19
|
+
return latest(...args);
|
|
20
|
+
};
|
|
21
|
+
tracker.actions.set(name, action);
|
|
22
|
+
}
|
|
23
|
+
return action;
|
|
24
|
+
}
|
|
25
|
+
function proxyOf(tracker, read) {
|
|
26
|
+
return new Proxy({
|
|
27
|
+
...read.fields,
|
|
28
|
+
...read.actions
|
|
29
|
+
}, { get(_, key) {
|
|
30
|
+
const name = String(key);
|
|
31
|
+
if (read.actions[name]) return stableAction(tracker, name);
|
|
32
|
+
tracker.tracked.add(name);
|
|
33
|
+
return renderedOf(tracker).fields[name];
|
|
34
|
+
} });
|
|
35
|
+
}
|
|
36
|
+
/** Functions are stable, so only data fields count as changes. */
|
|
37
|
+
function trackedChanged(tracker, next) {
|
|
38
|
+
const previous = renderedOf(tracker).fields;
|
|
39
|
+
return [...tracker.tracked].some((name) => !Object.is(previous[name], next.fields[name]));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The state a render returns: the same object as the last render's unless
|
|
43
|
+
* a field the component read changed. Reads go through to this render's
|
|
44
|
+
* fields, so a field first read now is current, and is tracked from now on.
|
|
45
|
+
*/
|
|
46
|
+
function trackedState(tracker, read, refresh) {
|
|
47
|
+
if (tracker.proxy === void 0 || trackedChanged(tracker, read)) tracker.proxy = proxyOf(tracker, read);
|
|
48
|
+
tracker.rendered = {
|
|
49
|
+
...read,
|
|
50
|
+
refresh,
|
|
51
|
+
status: read.source.status
|
|
52
|
+
};
|
|
53
|
+
return tracker.proxy;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Whether a write changed what the component rendered: the thenable or its
|
|
57
|
+
* outcome, or a field it read. Fields it never read don't re-render it.
|
|
58
|
+
*/
|
|
59
|
+
function renderedChanged(tracker) {
|
|
60
|
+
const rendered = renderedOf(tracker);
|
|
61
|
+
const next = rendered.refresh();
|
|
62
|
+
return next.source !== rendered.source || next.source.status !== rendered.status || trackedChanged(tracker, next);
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { createTracker, renderedChanged, trackedState };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { QueryState, QueryValue, UseQueryOptions } from "./use-query.mjs";
|
|
2
|
+
import { InfiniteQueryFactory, QueryHandle } from "@raf-query/core";
|
|
3
|
+
//#region src/use-infinite-query.d.ts
|
|
4
|
+
/** The paging half of what `useInfiniteQuery` returns. */
|
|
5
|
+
export interface PageControls {
|
|
6
|
+
/** Load the page after the last one. Resolves when it's there. */
|
|
7
|
+
fetchNextPage(): Promise<void>;
|
|
8
|
+
/** Load the page before the first one. Resolves when it's there. */
|
|
9
|
+
fetchPreviousPage(): Promise<void>;
|
|
10
|
+
readonly hasNextPage: boolean;
|
|
11
|
+
readonly hasPreviousPage: boolean;
|
|
12
|
+
readonly isFetchingNextPage: boolean;
|
|
13
|
+
readonly isFetchingPreviousPage: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** What `useInfiniteQuery` returns next to the data. */
|
|
16
|
+
export interface InfiniteQueryState<TFactory extends InfiniteQueryFactory = InfiniteQueryFactory> extends PageControls, QueryState<TFactory> {}
|
|
17
|
+
/**
|
|
18
|
+
* Read a paged query (`infiniteQueryOptions`) by its handle. The data is
|
|
19
|
+
* `{ pages, pageParams }`, and its state loads more pages.
|
|
20
|
+
*
|
|
21
|
+
* ```tsx
|
|
22
|
+
* const [pagesThenable, pagesState] = useInfiniteQuery(commentsQuery, handles.comments);
|
|
23
|
+
* const pages = use(pagesThenable);
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function useInfiniteQuery<TFactory extends InfiniteQueryFactory>(factory: TFactory, handle: QueryHandle<TFactory>, options?: UseQueryOptions): readonly [QueryValue<TFactory>, InfiniteQueryState<TFactory>];
|
|
27
|
+
//#endregion
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { settled, useQueryRead } from "./use-query.mjs";
|
|
2
|
+
import { fetchEntry, pageParamAfter } from "@raf-query/core/internal";
|
|
3
|
+
//#region src/use-infinite-query.ts
|
|
4
|
+
function hasPage(entry, state, direction) {
|
|
5
|
+
return state.hasData && pageParamAfter(entry.options, state.data, direction) !== void 0;
|
|
6
|
+
}
|
|
7
|
+
function pageFields(entry, state) {
|
|
8
|
+
return {
|
|
9
|
+
actions: {
|
|
10
|
+
fetchNextPage: () => settled(fetchEntry(entry, { direction: "next" })),
|
|
11
|
+
fetchPreviousPage: () => settled(fetchEntry(entry, { direction: "previous" }))
|
|
12
|
+
},
|
|
13
|
+
fields: {
|
|
14
|
+
hasNextPage: hasPage(entry, state, "next"),
|
|
15
|
+
hasPreviousPage: hasPage(entry, state, "previous"),
|
|
16
|
+
isFetchingNextPage: state.fetchingDirection === "next",
|
|
17
|
+
isFetchingPreviousPage: state.fetchingDirection === "previous"
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Read a paged query (`infiniteQueryOptions`) by its handle. The data is
|
|
23
|
+
* `{ pages, pageParams }`, and its state loads more pages.
|
|
24
|
+
*
|
|
25
|
+
* ```tsx
|
|
26
|
+
* const [pagesThenable, pagesState] = useInfiniteQuery(commentsQuery, handles.comments);
|
|
27
|
+
* const pages = use(pagesThenable);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
function useInfiniteQuery(factory, handle, options) {
|
|
31
|
+
const read = useQueryRead(factory, handle, options, pageFields);
|
|
32
|
+
return [read.thenable, read.controls];
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { useInfiniteQuery };
|
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Group } from "@raf-query/core";
|
|
2
|
+
import { AnyFactory, AnyTarget } from "@raf-query/core/internal";
|
|
2
3
|
//#region src/use-is-fetching.d.ts
|
|
3
4
|
/**
|
|
4
|
-
* How many queries have a request in flight: all of them, or
|
|
5
|
-
*
|
|
6
|
-
* while hydrating, so the
|
|
7
|
-
*
|
|
8
|
-
* You probably don't need this. Prefer the router's pending UI for
|
|
9
|
-
* navigations (`useRouterState` / `pendingComponent` in TanStack Router,
|
|
10
|
-
* `useLinkStatus` in Next.js) and per-query `controls.isFetching` for
|
|
11
|
-
* background refetches. Kept for TanStack Query compatibility when you want
|
|
12
|
-
* one global count of every in-flight request:
|
|
13
|
-
*
|
|
14
|
-
* ```tsx
|
|
15
|
-
* const fetching = useIsFetching();
|
|
16
|
-
* return fetching > 0 ? <ProgressBar /> : null;
|
|
17
|
-
* ```
|
|
5
|
+
* How many queries have a request in flight: all of them, or a factory's,
|
|
6
|
+
* a group's or a target's. For a global loading indicator. Always `0` on
|
|
7
|
+
* the server and while hydrating, so the HTML matches the first browser render.
|
|
18
8
|
*/
|
|
19
|
-
export declare function useIsFetching(
|
|
9
|
+
export declare function useIsFetching(target?: AnyFactory | AnyTarget | Group): number;
|
|
20
10
|
//#endregion
|