@effector-tanstack-query/react 0.2.0 → 0.3.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.
- package/dist/compat.cjs +47 -0
- package/dist/compat.cjs.map +1 -0
- package/dist/compat.d.cts +53 -0
- package/dist/compat.d.ts +53 -0
- package/dist/compat.js +25 -0
- package/dist/compat.js.map +1 -0
- package/dist/index.cjs +101 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +101 -33
- package/dist/index.js.map +1 -1
- package/package.json +21 -2
- package/src/compat.tsx +83 -0
- package/src/index.ts +149 -57
package/dist/compat.cjs
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var effectorReact = require('effector-react');
|
|
6
|
+
var reactQuery = require('@tanstack/react-query');
|
|
7
|
+
var core = require('@effector-tanstack-query/core');
|
|
8
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
+
|
|
10
|
+
function _interopNamespace(e) {
|
|
11
|
+
if (e && e.__esModule) return e;
|
|
12
|
+
var n = Object.create(null);
|
|
13
|
+
if (e) {
|
|
14
|
+
Object.keys(e).forEach(function (k) {
|
|
15
|
+
if (k !== 'default') {
|
|
16
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
17
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
get: function () { return e[k]; }
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
n.default = e;
|
|
25
|
+
return Object.freeze(n);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
29
|
+
|
|
30
|
+
function QueryClientCompatProvider({
|
|
31
|
+
children,
|
|
32
|
+
defaultOptions
|
|
33
|
+
}) {
|
|
34
|
+
const fromEffector = effectorReact.useUnit(core.$queryClient);
|
|
35
|
+
const [serverFallback] = React__namespace.useState(
|
|
36
|
+
() => reactQuery.isServer ? new reactQuery.QueryClient({ defaultOptions }) : null
|
|
37
|
+
);
|
|
38
|
+
const qc = fromEffector ?? serverFallback;
|
|
39
|
+
if (!qc) {
|
|
40
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
41
|
+
}
|
|
42
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: qc, children });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
exports.QueryClientCompatProvider = QueryClientCompatProvider;
|
|
46
|
+
//# sourceMappingURL=compat.cjs.map
|
|
47
|
+
//# sourceMappingURL=compat.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/compat.tsx"],"names":["useUnit","$queryClient","React","isServer","QueryClient","jsx","QueryClientProvider"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DO,SAAS,yBAAA,CAA0B;AAAA,EACxC,QAAA;AAAA,EACA;AACF,CAAA,EAAuD;AACrD,EAAA,MAAM,YAAA,GAAeA,sBAAQC,iBAAY,CAAA;AACzC,EAAA,MAAM,CAAC,cAAc,CAAA,GAAUC,gBAAA,CAAA,QAAA;AAAA,IAA6B,MAC1DC,mBAAA,GAAW,IAAIC,uBAAY,EAAE,cAAA,EAAgB,CAAA,GAAI;AAAA,GACnD;AAEA,EAAA,MAAM,KAAK,YAAA,IAAgB,cAAA;AAC3B,EAAA,IAAI,CAAC,EAAA,EAAI;AAMP,IAAA,6DAAU,QAAA,EAAS,CAAA;AAAA,EACrB;AAEA,EAAA,uBAAOC,cAAA,CAACC,8BAAA,EAAA,EAAoB,MAAA,EAAQ,EAAA,EAAK,QAAA,EAAS,CAAA;AACpD","file":"compat.cjs","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { useUnit } from 'effector-react'\nimport {\n isServer,\n QueryClient,\n QueryClientProvider,\n} from '@tanstack/react-query'\nimport { $queryClient } from '@effector-tanstack-query/core'\n\ntype DefaultOptions = NonNullable<\n ConstructorParameters<typeof QueryClient>[0]\n>['defaultOptions']\n\nexport interface QueryClientCompatProviderProps {\n children: React.ReactNode\n /**\n * `defaultOptions` for the per-render server fallback `QueryClient`\n * (see below). Has no effect on the client — the browser singleton\n * built in your own top-level `<EffectorNext>` setup is what `useQuery`\n * from `@tanstack/react-query` resolves to there. Keep these aligned\n * with your `createQuery({ staleTime, retry, ... })` definitions so\n * server-rendered HTML matches what the client will produce after\n * hydration.\n */\n defaultOptions?: DefaultOptions\n}\n\n/**\n * SSR-safe `QueryClientProvider` bridge for users mixing\n * `@tanstack/react-query` and `@effector-tanstack-query` — the typical\n * \"we're migrating page by page\" case.\n *\n * Why a dedicated component:\n *\n * - **Client**: `useUnit($queryClient)` returns the singleton browser\n * `QueryClient` already mounted by your top-level provider (whichever\n * module calls `setQueryClient(qc)` + `allSettled($queryClient, ...)`\n * for `getClientScope()`). Handing that same instance to\n * `<QueryClientProvider>` means **both APIs share a cache**: prefetch\n * with one, read with the other; `setQueryData` /\n * `invalidateQueries` from one re-renders the other.\n *\n * - **Server (RSC)**: `$queryClient` is `serialize: 'ignore'` (its\n * value is a class instance — not JSON-safe — and per-request\n * QueryClients must NEVER be a module-level singleton on the\n * server). So inside the RSC rendering scope produced by\n * `<EffectorNext values={serialize(scope)}>`, `useUnit($queryClient)`\n * resolves to `null`. We fall back to a throwaway per-render\n * `QueryClient` solely so vanilla `useQuery` has a provider during\n * the server pass. Pair this component with `<HydrationBoundary>`\n * from `@tanstack/react-query` — the boundary will hydrate this\n * fallback client with the prefetched cache, so server-rendered\n * HTML shows data instead of a loading flash. On client hydration\n * the provider re-renders with the singleton browser client and\n * react-query re-subscribes its observers.\n *\n * `useState` makes the fallback per-component-instance (one per render\n * tree), not a module-level singleton — concurrent SSR requests don't\n * share it.\n */\nexport function QueryClientCompatProvider({\n children,\n defaultOptions,\n}: QueryClientCompatProviderProps): React.ReactElement {\n const fromEffector = useUnit($queryClient)\n const [serverFallback] = React.useState<QueryClient | null>(() =>\n isServer ? new QueryClient({ defaultOptions }) : null,\n )\n\n const qc = fromEffector ?? serverFallback\n if (!qc) {\n // Defensive: on the client `fromEffector` is set synchronously by\n // your top-level provider before any render, so this branch is\n // unreachable in practice. If it fires, rendering children without\n // a provider would crash any descendant `useQuery` from\n // `@tanstack/react-query`.\n return <>{children}</>\n }\n\n return <QueryClientProvider client={qc}>{children}</QueryClientProvider>\n}\n"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
3
|
+
|
|
4
|
+
type DefaultOptions = NonNullable<ConstructorParameters<typeof QueryClient>[0]>['defaultOptions'];
|
|
5
|
+
interface QueryClientCompatProviderProps {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
/**
|
|
8
|
+
* `defaultOptions` for the per-render server fallback `QueryClient`
|
|
9
|
+
* (see below). Has no effect on the client — the browser singleton
|
|
10
|
+
* built in your own top-level `<EffectorNext>` setup is what `useQuery`
|
|
11
|
+
* from `@tanstack/react-query` resolves to there. Keep these aligned
|
|
12
|
+
* with your `createQuery({ staleTime, retry, ... })` definitions so
|
|
13
|
+
* server-rendered HTML matches what the client will produce after
|
|
14
|
+
* hydration.
|
|
15
|
+
*/
|
|
16
|
+
defaultOptions?: DefaultOptions;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* SSR-safe `QueryClientProvider` bridge for users mixing
|
|
20
|
+
* `@tanstack/react-query` and `@effector-tanstack-query` — the typical
|
|
21
|
+
* "we're migrating page by page" case.
|
|
22
|
+
*
|
|
23
|
+
* Why a dedicated component:
|
|
24
|
+
*
|
|
25
|
+
* - **Client**: `useUnit($queryClient)` returns the singleton browser
|
|
26
|
+
* `QueryClient` already mounted by your top-level provider (whichever
|
|
27
|
+
* module calls `setQueryClient(qc)` + `allSettled($queryClient, ...)`
|
|
28
|
+
* for `getClientScope()`). Handing that same instance to
|
|
29
|
+
* `<QueryClientProvider>` means **both APIs share a cache**: prefetch
|
|
30
|
+
* with one, read with the other; `setQueryData` /
|
|
31
|
+
* `invalidateQueries` from one re-renders the other.
|
|
32
|
+
*
|
|
33
|
+
* - **Server (RSC)**: `$queryClient` is `serialize: 'ignore'` (its
|
|
34
|
+
* value is a class instance — not JSON-safe — and per-request
|
|
35
|
+
* QueryClients must NEVER be a module-level singleton on the
|
|
36
|
+
* server). So inside the RSC rendering scope produced by
|
|
37
|
+
* `<EffectorNext values={serialize(scope)}>`, `useUnit($queryClient)`
|
|
38
|
+
* resolves to `null`. We fall back to a throwaway per-render
|
|
39
|
+
* `QueryClient` solely so vanilla `useQuery` has a provider during
|
|
40
|
+
* the server pass. Pair this component with `<HydrationBoundary>`
|
|
41
|
+
* from `@tanstack/react-query` — the boundary will hydrate this
|
|
42
|
+
* fallback client with the prefetched cache, so server-rendered
|
|
43
|
+
* HTML shows data instead of a loading flash. On client hydration
|
|
44
|
+
* the provider re-renders with the singleton browser client and
|
|
45
|
+
* react-query re-subscribes its observers.
|
|
46
|
+
*
|
|
47
|
+
* `useState` makes the fallback per-component-instance (one per render
|
|
48
|
+
* tree), not a module-level singleton — concurrent SSR requests don't
|
|
49
|
+
* share it.
|
|
50
|
+
*/
|
|
51
|
+
declare function QueryClientCompatProvider({ children, defaultOptions, }: QueryClientCompatProviderProps): React.ReactElement;
|
|
52
|
+
|
|
53
|
+
export { QueryClientCompatProvider, type QueryClientCompatProviderProps };
|
package/dist/compat.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
3
|
+
|
|
4
|
+
type DefaultOptions = NonNullable<ConstructorParameters<typeof QueryClient>[0]>['defaultOptions'];
|
|
5
|
+
interface QueryClientCompatProviderProps {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
/**
|
|
8
|
+
* `defaultOptions` for the per-render server fallback `QueryClient`
|
|
9
|
+
* (see below). Has no effect on the client — the browser singleton
|
|
10
|
+
* built in your own top-level `<EffectorNext>` setup is what `useQuery`
|
|
11
|
+
* from `@tanstack/react-query` resolves to there. Keep these aligned
|
|
12
|
+
* with your `createQuery({ staleTime, retry, ... })` definitions so
|
|
13
|
+
* server-rendered HTML matches what the client will produce after
|
|
14
|
+
* hydration.
|
|
15
|
+
*/
|
|
16
|
+
defaultOptions?: DefaultOptions;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* SSR-safe `QueryClientProvider` bridge for users mixing
|
|
20
|
+
* `@tanstack/react-query` and `@effector-tanstack-query` — the typical
|
|
21
|
+
* "we're migrating page by page" case.
|
|
22
|
+
*
|
|
23
|
+
* Why a dedicated component:
|
|
24
|
+
*
|
|
25
|
+
* - **Client**: `useUnit($queryClient)` returns the singleton browser
|
|
26
|
+
* `QueryClient` already mounted by your top-level provider (whichever
|
|
27
|
+
* module calls `setQueryClient(qc)` + `allSettled($queryClient, ...)`
|
|
28
|
+
* for `getClientScope()`). Handing that same instance to
|
|
29
|
+
* `<QueryClientProvider>` means **both APIs share a cache**: prefetch
|
|
30
|
+
* with one, read with the other; `setQueryData` /
|
|
31
|
+
* `invalidateQueries` from one re-renders the other.
|
|
32
|
+
*
|
|
33
|
+
* - **Server (RSC)**: `$queryClient` is `serialize: 'ignore'` (its
|
|
34
|
+
* value is a class instance — not JSON-safe — and per-request
|
|
35
|
+
* QueryClients must NEVER be a module-level singleton on the
|
|
36
|
+
* server). So inside the RSC rendering scope produced by
|
|
37
|
+
* `<EffectorNext values={serialize(scope)}>`, `useUnit($queryClient)`
|
|
38
|
+
* resolves to `null`. We fall back to a throwaway per-render
|
|
39
|
+
* `QueryClient` solely so vanilla `useQuery` has a provider during
|
|
40
|
+
* the server pass. Pair this component with `<HydrationBoundary>`
|
|
41
|
+
* from `@tanstack/react-query` — the boundary will hydrate this
|
|
42
|
+
* fallback client with the prefetched cache, so server-rendered
|
|
43
|
+
* HTML shows data instead of a loading flash. On client hydration
|
|
44
|
+
* the provider re-renders with the singleton browser client and
|
|
45
|
+
* react-query re-subscribes its observers.
|
|
46
|
+
*
|
|
47
|
+
* `useState` makes the fallback per-component-instance (one per render
|
|
48
|
+
* tree), not a module-level singleton — concurrent SSR requests don't
|
|
49
|
+
* share it.
|
|
50
|
+
*/
|
|
51
|
+
declare function QueryClientCompatProvider({ children, defaultOptions, }: QueryClientCompatProviderProps): React.ReactElement;
|
|
52
|
+
|
|
53
|
+
export { QueryClientCompatProvider, type QueryClientCompatProviderProps };
|
package/dist/compat.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { useUnit } from 'effector-react';
|
|
4
|
+
import { QueryClient, isServer, QueryClientProvider } from '@tanstack/react-query';
|
|
5
|
+
import { $queryClient } from '@effector-tanstack-query/core';
|
|
6
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
function QueryClientCompatProvider({
|
|
9
|
+
children,
|
|
10
|
+
defaultOptions
|
|
11
|
+
}) {
|
|
12
|
+
const fromEffector = useUnit($queryClient);
|
|
13
|
+
const [serverFallback] = React.useState(
|
|
14
|
+
() => isServer ? new QueryClient({ defaultOptions }) : null
|
|
15
|
+
);
|
|
16
|
+
const qc = fromEffector ?? serverFallback;
|
|
17
|
+
if (!qc) {
|
|
18
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
19
|
+
}
|
|
20
|
+
return /* @__PURE__ */ jsx(QueryClientProvider, { client: qc, children });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { QueryClientCompatProvider };
|
|
24
|
+
//# sourceMappingURL=compat.js.map
|
|
25
|
+
//# sourceMappingURL=compat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/compat.tsx"],"names":[],"mappings":";;;;;;AA8DO,SAAS,yBAAA,CAA0B;AAAA,EACxC,QAAA;AAAA,EACA;AACF,CAAA,EAAuD;AACrD,EAAA,MAAM,YAAA,GAAe,QAAQ,YAAY,CAAA;AACzC,EAAA,MAAM,CAAC,cAAc,CAAA,GAAU,KAAA,CAAA,QAAA;AAAA,IAA6B,MAC1D,QAAA,GAAW,IAAI,YAAY,EAAE,cAAA,EAAgB,CAAA,GAAI;AAAA,GACnD;AAEA,EAAA,MAAM,KAAK,YAAA,IAAgB,cAAA;AAC3B,EAAA,IAAI,CAAC,EAAA,EAAI;AAMP,IAAA,uCAAU,QAAA,EAAS,CAAA;AAAA,EACrB;AAEA,EAAA,uBAAO,GAAA,CAAC,mBAAA,EAAA,EAAoB,MAAA,EAAQ,EAAA,EAAK,QAAA,EAAS,CAAA;AACpD","file":"compat.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { useUnit } from 'effector-react'\nimport {\n isServer,\n QueryClient,\n QueryClientProvider,\n} from '@tanstack/react-query'\nimport { $queryClient } from '@effector-tanstack-query/core'\n\ntype DefaultOptions = NonNullable<\n ConstructorParameters<typeof QueryClient>[0]\n>['defaultOptions']\n\nexport interface QueryClientCompatProviderProps {\n children: React.ReactNode\n /**\n * `defaultOptions` for the per-render server fallback `QueryClient`\n * (see below). Has no effect on the client — the browser singleton\n * built in your own top-level `<EffectorNext>` setup is what `useQuery`\n * from `@tanstack/react-query` resolves to there. Keep these aligned\n * with your `createQuery({ staleTime, retry, ... })` definitions so\n * server-rendered HTML matches what the client will produce after\n * hydration.\n */\n defaultOptions?: DefaultOptions\n}\n\n/**\n * SSR-safe `QueryClientProvider` bridge for users mixing\n * `@tanstack/react-query` and `@effector-tanstack-query` — the typical\n * \"we're migrating page by page\" case.\n *\n * Why a dedicated component:\n *\n * - **Client**: `useUnit($queryClient)` returns the singleton browser\n * `QueryClient` already mounted by your top-level provider (whichever\n * module calls `setQueryClient(qc)` + `allSettled($queryClient, ...)`\n * for `getClientScope()`). Handing that same instance to\n * `<QueryClientProvider>` means **both APIs share a cache**: prefetch\n * with one, read with the other; `setQueryData` /\n * `invalidateQueries` from one re-renders the other.\n *\n * - **Server (RSC)**: `$queryClient` is `serialize: 'ignore'` (its\n * value is a class instance — not JSON-safe — and per-request\n * QueryClients must NEVER be a module-level singleton on the\n * server). So inside the RSC rendering scope produced by\n * `<EffectorNext values={serialize(scope)}>`, `useUnit($queryClient)`\n * resolves to `null`. We fall back to a throwaway per-render\n * `QueryClient` solely so vanilla `useQuery` has a provider during\n * the server pass. Pair this component with `<HydrationBoundary>`\n * from `@tanstack/react-query` — the boundary will hydrate this\n * fallback client with the prefetched cache, so server-rendered\n * HTML shows data instead of a loading flash. On client hydration\n * the provider re-renders with the singleton browser client and\n * react-query re-subscribes its observers.\n *\n * `useState` makes the fallback per-component-instance (one per render\n * tree), not a module-level singleton — concurrent SSR requests don't\n * share it.\n */\nexport function QueryClientCompatProvider({\n children,\n defaultOptions,\n}: QueryClientCompatProviderProps): React.ReactElement {\n const fromEffector = useUnit($queryClient)\n const [serverFallback] = React.useState<QueryClient | null>(() =>\n isServer ? new QueryClient({ defaultOptions }) : null,\n )\n\n const qc = fromEffector ?? serverFallback\n if (!qc) {\n // Defensive: on the client `fromEffector` is set synchronously by\n // your top-level provider before any render, so this branch is\n // unreachable in practice. If it fires, rendering children without\n // a provider would crash any descendant `useQuery` from\n // `@tanstack/react-query`.\n return <>{children}</>\n }\n\n return <QueryClientProvider client={qc}>{children}</QueryClientProvider>\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -112,7 +112,10 @@ function useInfiniteQuery(query) {
|
|
|
112
112
|
}
|
|
113
113
|
function useObserverRerender(observer) {
|
|
114
114
|
const [, forceRender] = React__namespace.useReducer((x) => x + 1, 0);
|
|
115
|
-
React__namespace.useEffect(() =>
|
|
115
|
+
React__namespace.useEffect(() => {
|
|
116
|
+
if (!observer) return;
|
|
117
|
+
return observer.subscribe(forceRender);
|
|
118
|
+
}, [observer]);
|
|
116
119
|
}
|
|
117
120
|
function useSuspenseQuery(query) {
|
|
118
121
|
const mount = effectorReact.useUnit(query.mounted);
|
|
@@ -124,21 +127,49 @@ function useSuspenseQuery(query) {
|
|
|
124
127
|
}, [mount, unmount]);
|
|
125
128
|
const observer = useSuspenseObserver(query);
|
|
126
129
|
useObserverRerender(observer);
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
const state = effectorReact.useUnit({
|
|
131
|
+
data: query.$data,
|
|
132
|
+
error: query.$error,
|
|
133
|
+
status: query.$status,
|
|
134
|
+
isFetching: query.$isFetching,
|
|
135
|
+
isPlaceholderData: query.$isPlaceholderData,
|
|
136
|
+
fetchStatus: query.$fetchStatus
|
|
137
|
+
});
|
|
138
|
+
if (observer) {
|
|
139
|
+
const result = observer.getOptimisticResult(observer.options);
|
|
140
|
+
if (result.status === "error") throw result.error;
|
|
141
|
+
if (result.status === "pending") {
|
|
142
|
+
throw observer.fetchOptimistic(observer.options);
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
data: result.data,
|
|
146
|
+
error: result.error,
|
|
147
|
+
status: "success",
|
|
148
|
+
isPending: false,
|
|
149
|
+
isSuccess: true,
|
|
150
|
+
isError: false,
|
|
151
|
+
isFetching: result.isFetching,
|
|
152
|
+
isPlaceholderData: result.isPlaceholderData,
|
|
153
|
+
fetchStatus: result.fetchStatus,
|
|
154
|
+
refresh
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (state.status === "error") throw state.error;
|
|
158
|
+
if (state.status === "pending") {
|
|
159
|
+
throw new Error(
|
|
160
|
+
"[@effector-tanstack-query/react] useSuspenseQuery: no QueryClient is set. Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] })."
|
|
161
|
+
);
|
|
131
162
|
}
|
|
132
163
|
return {
|
|
133
|
-
data:
|
|
134
|
-
error:
|
|
164
|
+
data: state.data,
|
|
165
|
+
error: state.error,
|
|
135
166
|
status: "success",
|
|
136
167
|
isPending: false,
|
|
137
168
|
isSuccess: true,
|
|
138
169
|
isError: false,
|
|
139
|
-
isFetching:
|
|
140
|
-
isPlaceholderData:
|
|
141
|
-
fetchStatus:
|
|
170
|
+
isFetching: state.isFetching,
|
|
171
|
+
isPlaceholderData: state.isPlaceholderData,
|
|
172
|
+
fetchStatus: state.fetchStatus,
|
|
142
173
|
refresh
|
|
143
174
|
};
|
|
144
175
|
}
|
|
@@ -154,28 +185,71 @@ function useSuspenseInfiniteQuery(query) {
|
|
|
154
185
|
}, [mount, unmount]);
|
|
155
186
|
const observer = useSuspenseObserver(query);
|
|
156
187
|
useObserverRerender(observer);
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
188
|
+
const state = effectorReact.useUnit({
|
|
189
|
+
data: query.$data,
|
|
190
|
+
error: query.$error,
|
|
191
|
+
status: query.$status,
|
|
192
|
+
isFetching: query.$isFetching,
|
|
193
|
+
isPlaceholderData: query.$isPlaceholderData,
|
|
194
|
+
fetchStatus: query.$fetchStatus,
|
|
195
|
+
hasNextPage: query.$hasNextPage,
|
|
196
|
+
hasPreviousPage: query.$hasPreviousPage,
|
|
197
|
+
isFetchingNextPage: query.$isFetchingNextPage,
|
|
198
|
+
isFetchingPreviousPage: query.$isFetchingPreviousPage,
|
|
199
|
+
isFetchNextPageError: query.$isFetchNextPageError,
|
|
200
|
+
isFetchPreviousPageError: query.$isFetchPreviousPageError
|
|
201
|
+
});
|
|
202
|
+
if (observer) {
|
|
203
|
+
const obs = observer;
|
|
204
|
+
const result = obs.getOptimisticResult(obs.options);
|
|
205
|
+
if (result.status === "error") throw result.error;
|
|
206
|
+
if (result.status === "pending") {
|
|
207
|
+
throw obs.fetchOptimistic(obs.options);
|
|
208
|
+
}
|
|
209
|
+
const r = result;
|
|
210
|
+
return {
|
|
211
|
+
data: r.data,
|
|
212
|
+
error: r.error,
|
|
213
|
+
status: "success",
|
|
214
|
+
isPending: false,
|
|
215
|
+
isSuccess: true,
|
|
216
|
+
isError: false,
|
|
217
|
+
isFetching: r.isFetching,
|
|
218
|
+
isPlaceholderData: r.isPlaceholderData,
|
|
219
|
+
fetchStatus: r.fetchStatus,
|
|
220
|
+
hasNextPage: r.hasNextPage,
|
|
221
|
+
hasPreviousPage: r.hasPreviousPage,
|
|
222
|
+
isFetchingNextPage: r.isFetchingNextPage,
|
|
223
|
+
isFetchingPreviousPage: r.isFetchingPreviousPage,
|
|
224
|
+
isFetchNextPageError: r.isFetchNextPageError,
|
|
225
|
+
isFetchPreviousPageError: r.isFetchPreviousPageError,
|
|
226
|
+
refresh,
|
|
227
|
+
fetchNextPage,
|
|
228
|
+
fetchPreviousPage
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
if (state.status === "error") throw state.error;
|
|
232
|
+
if (state.status === "pending") {
|
|
233
|
+
throw new Error(
|
|
234
|
+
"[@effector-tanstack-query/react] useSuspenseInfiniteQuery: no QueryClient is set. Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] })."
|
|
235
|
+
);
|
|
161
236
|
}
|
|
162
|
-
const r = result;
|
|
163
237
|
return {
|
|
164
|
-
data:
|
|
165
|
-
error:
|
|
238
|
+
data: state.data,
|
|
239
|
+
error: state.error,
|
|
166
240
|
status: "success",
|
|
167
241
|
isPending: false,
|
|
168
242
|
isSuccess: true,
|
|
169
243
|
isError: false,
|
|
170
|
-
isFetching:
|
|
171
|
-
isPlaceholderData:
|
|
172
|
-
fetchStatus:
|
|
173
|
-
hasNextPage:
|
|
174
|
-
hasPreviousPage:
|
|
175
|
-
isFetchingNextPage:
|
|
176
|
-
isFetchingPreviousPage:
|
|
177
|
-
isFetchNextPageError:
|
|
178
|
-
isFetchPreviousPageError:
|
|
244
|
+
isFetching: state.isFetching,
|
|
245
|
+
isPlaceholderData: state.isPlaceholderData,
|
|
246
|
+
fetchStatus: state.fetchStatus,
|
|
247
|
+
hasNextPage: state.hasNextPage,
|
|
248
|
+
hasPreviousPage: state.hasPreviousPage,
|
|
249
|
+
isFetchingNextPage: state.isFetchingNextPage,
|
|
250
|
+
isFetchingPreviousPage: state.isFetchingPreviousPage,
|
|
251
|
+
isFetchNextPageError: state.isFetchNextPageError,
|
|
252
|
+
isFetchPreviousPageError: state.isFetchPreviousPageError,
|
|
179
253
|
refresh,
|
|
180
254
|
fetchNextPage,
|
|
181
255
|
fetchPreviousPage
|
|
@@ -195,13 +269,7 @@ function useSuspenseObserver(query) {
|
|
|
195
269
|
if (!transient) return;
|
|
196
270
|
transient.setOptions({ ...transient.options, queryKey, enabled });
|
|
197
271
|
}, [transient, queryKey, enabled]);
|
|
198
|
-
|
|
199
|
-
if (!observer) {
|
|
200
|
-
throw new Error(
|
|
201
|
-
"[@effector-tanstack-query/react] useSuspenseQuery: no QueryClient is set. Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] })."
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
return observer;
|
|
272
|
+
return observerInScope ?? transient;
|
|
205
273
|
}
|
|
206
274
|
|
|
207
275
|
exports.HydrationBoundary = HydrationBoundary;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["useUnit","$queryClient","React","hydrate"],"mappings":"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","file":"index.cjs","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { useUnit } from 'effector-react'\nimport { hydrate } from '@tanstack/query-core'\nimport type {\n DehydratedState,\n FetchStatus,\n HydrateOptions,\n MutateOptions,\n QueryStatus,\n} from '@tanstack/query-core'\nimport { $queryClient } from '@effector-tanstack-query/core'\nimport type {\n InfiniteQueryResult,\n MutationResult,\n MutationStatus,\n QueryResult,\n} from '@effector-tanstack-query/core'\n\nexport interface HydrationBoundaryProps {\n /**\n * Snapshot produced by `dehydrate(queryClient)` on the server. Re-applied\n * to the scope's `QueryClient` cache so observers mounted under this tree\n * read prefetched data instead of triggering fresh network requests.\n */\n state?: DehydratedState\n /** Forwarded to `hydrate(...)` — see `@tanstack/query-core` docs. */\n options?: HydrateOptions\n children?: React.ReactNode\n}\n\n/**\n * Merges a server-prefetched `DehydratedState` into the scope's\n * `QueryClient` cache.\n *\n * Mirrors `<HydrationBoundary>` from `@tanstack/react-query`: hydration\n * runs in `useMemo` so the merge happens during the render phase (children\n * see a populated cache on their first render, no flash). The hook\n * resolves the QueryClient via `useUnit($queryClient)` instead of\n * `useQueryClient()` — meaning each fork scope can have its own client\n * without an additional `<QueryClientProvider>` in the tree.\n *\n * `hydrate` is idempotent: re-rendering with the same `state` reference\n * is a no-op. Pass new `state` references on navigation to merge fresh\n * snapshots.\n *\n * Note: this only handles the QueryClient cache layer. Effector store\n * snapshots (e.g. `serialize(scope)`) flow through your existing\n * `<Provider>` / `<EffectorNext values>` layer — orthogonal concerns.\n */\nexport function HydrationBoundary({\n state,\n options,\n children,\n}: HydrationBoundaryProps): React.ReactElement {\n const queryClient = useUnit($queryClient)\n React.useMemo(() => {\n if (queryClient && state) hydrate(queryClient, state, options)\n }, [queryClient, state, options])\n return React.createElement(React.Fragment, null, children)\n}\n\nexport interface UseQueryResult<TData, TError = Error> {\n data: TData | undefined\n error: TError | null\n status: QueryStatus\n isPending: boolean\n isFetching: boolean\n isSuccess: boolean\n isError: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n refresh: () => void\n}\n\n/**\n * Subscribes a React component to a query, automatically calling\n * `mounted()` on mount and `unmounted()` on cleanup.\n */\nexport function useQuery<TData, TError = Error>(\n query: QueryResult<TData, TError>,\n): UseQueryResult<TData, TError> {\n const state = useUnit({\n data: query.$data,\n error: query.$error,\n status: query.$status,\n isPending: query.$isPending,\n isFetching: query.$isFetching,\n isSuccess: query.$isSuccess,\n isError: query.$isError,\n isPlaceholderData: query.$isPlaceholderData,\n fetchStatus: query.$fetchStatus,\n })\n\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n return { ...state, refresh }\n}\n\nexport interface UseMutationResult<TData, TError, TVariables> {\n data: TData | undefined\n error: TError | null\n status: MutationStatus\n variables: TVariables | undefined\n isPaused: boolean\n isPending: boolean\n isSuccess: boolean\n isError: boolean\n isIdle: boolean\n mutate: (variables: TVariables) => void\n /**\n * Trigger the mutation with per-call callbacks layered on top of the\n * observer-level ones (`onSuccess` / `onError` / `onSettled` in\n * `createMutation` options). Use this for component-local reactions\n * that don't fit module-level `sample` wiring — navigation after success,\n * one-shot toasts, etc.\n */\n mutateWith: (args: {\n variables: TVariables\n onSuccess?: MutateOptions<TData, TError, TVariables>['onSuccess']\n onError?: MutateOptions<TData, TError, TVariables>['onError']\n onSettled?: MutateOptions<TData, TError, TVariables>['onSettled']\n }) => void\n reset: () => void\n}\n\n/**\n * Subscribes a React component to a mutation, automatically calling\n * `start()` on mount and `unmounted()` on cleanup so the queryClient can\n * garbage-collect the mutation entry once no observers remain.\n */\nexport function useMutation<TData = unknown, TError = Error, TVariables = void>(\n mutation: MutationResult<TData, TError, TVariables>,\n): UseMutationResult<TData, TError, TVariables> {\n const state = useUnit({\n data: mutation.$data,\n error: mutation.$error,\n status: mutation.$status,\n variables: mutation.$variables,\n isPaused: mutation.$isPaused,\n isPending: mutation.$isPending,\n isSuccess: mutation.$isSuccess,\n isError: mutation.$isError,\n isIdle: mutation.$isIdle,\n })\n\n const start = useUnit(mutation.start)\n const unmount = useUnit(mutation.unmounted)\n const mutate = useUnit(mutation.mutate)\n const mutateWith = useUnit(mutation.mutateWith)\n const reset = useUnit(mutation.reset)\n\n React.useEffect(() => {\n start()\n return () => unmount()\n }, [start, unmount])\n\n return { ...state, mutate, mutateWith, reset }\n}\n\nexport interface UseInfiniteQueryResult<TData, TError> {\n data: TData | undefined\n error: TError | null\n status: QueryStatus\n isPending: boolean\n isFetching: boolean\n isSuccess: boolean\n isError: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n hasNextPage: boolean\n hasPreviousPage: boolean\n isFetchingNextPage: boolean\n isFetchingPreviousPage: boolean\n isFetchNextPageError: boolean\n isFetchPreviousPageError: boolean\n refresh: () => void\n fetchNextPage: () => void\n fetchPreviousPage: () => void\n}\n\n/**\n * Subscribes a React component to an infinite query, with auto mount/unmount\n * lifecycle and bound `fetchNextPage` / `fetchPreviousPage` callbacks.\n */\nexport function useInfiniteQuery<TData, TError = Error, TPageParam = unknown>(\n query: InfiniteQueryResult<TData, TError, TPageParam>,\n): UseInfiniteQueryResult<TData, TError> {\n const state = useUnit({\n data: query.$data,\n error: query.$error,\n status: query.$status,\n isPending: query.$isPending,\n isFetching: query.$isFetching,\n isSuccess: query.$isSuccess,\n isError: query.$isError,\n isPlaceholderData: query.$isPlaceholderData,\n fetchStatus: query.$fetchStatus,\n hasNextPage: query.$hasNextPage,\n hasPreviousPage: query.$hasPreviousPage,\n isFetchingNextPage: query.$isFetchingNextPage,\n isFetchingPreviousPage: query.$isFetchingPreviousPage,\n isFetchNextPageError: query.$isFetchNextPageError,\n isFetchPreviousPageError: query.$isFetchPreviousPageError,\n })\n\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n const fetchNextPage = useUnit(query.fetchNextPage)\n const fetchPreviousPage = useUnit(query.fetchPreviousPage)\n\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n return { ...state, refresh, fetchNextPage, fetchPreviousPage }\n}\n\n// Suspense data path: read from a per-scope observer.\n//\n// The scope mount chain runs in useEffect, which is skipped while a component\n// is suspended — so on the very first render the scope's `$observer` may be\n// null. To get synchronous access to the observer's promise during suspense,\n// we construct a transient observer via the factory's hidden\n// `__createObserver(qc, { queryKey, enabled })` helper. The transient observer\n// reads from / writes to the same queryClient cache as the eventual scope\n// observer (which is created when mountFx runs after useEffect commits).\n//\n// The mount/unmount effect is still wired up so that other consumers reading\n// the same query through `useUnit` / `useQuery` see updates in scope state.\n\nfunction useObserverRerender(observer: {\n subscribe: (cb: () => void) => () => void\n}): void {\n const [, forceRender] = React.useReducer((x: number) => x + 1, 0)\n React.useEffect(() => observer.subscribe(forceRender), [observer])\n}\n\ninterface SuspenseFactory<TObserver> {\n __createObserver(\n qc: import('@tanstack/query-core').QueryClient,\n init: { queryKey: unknown; enabled: boolean },\n ): TObserver\n __resolvedKey: import('effector').Store<unknown>\n __enabled: import('effector').Store<boolean>\n}\n\nexport interface UseSuspenseQueryResult<TData, TError = Error> {\n /** Resolved query data — non-nullable inside the rendered subtree (Suspense\n * absorbed the pending state). */\n data: TData\n /** Always `null` past the Suspense gate; errors are thrown to the nearest\n * `<ErrorBoundary>`. Typed as `TError | null` for consistency with\n * `useQuery` so the same destructure works in both. */\n error: TError | null\n status: 'success'\n isPending: false\n isSuccess: true\n isError: false\n /** `true` while a background refetch is running. Use for refresh spinners. */\n isFetching: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n refresh: () => void\n}\n\n/**\n * Reads a query for use inside a `<Suspense>` boundary. While the query is\n * pending, throws an inflight promise (queryClient-deduplicated). On error,\n * throws the error — catch with `<ErrorBoundary>`. Returns the same shape as\n * `useQuery`, but with `data` narrowed to non-nullable `TData` since the\n * pending state is impossible past the Suspense gate.\n */\nexport function useSuspenseQuery<TData, TError = Error>(\n query: QueryResult<TData, TError>,\n): UseSuspenseQueryResult<TData, TError> {\n // Auto-mount lifecycle so concurrent consumers (useUnit / useQuery) reading\n // the same query through the effector scope stay in sync.\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n const observer = useSuspenseObserver(query)\n\n useObserverRerender(observer)\n\n const result = observer.getOptimisticResult(observer.options as any)\n\n if (result.status === 'error') throw result.error\n if (result.status === 'pending') {\n throw observer.fetchOptimistic(observer.options as any)\n }\n\n // Read all secondary fields from the observer result, not the effector\n // stores: stores are only populated after mountFx fires from useEffect,\n // which on the very first successful render hasn't run yet. The observer\n // result is always live and consistent.\n return {\n data: result.data as TData,\n error: result.error as TError | null,\n status: 'success',\n isPending: false,\n isSuccess: true,\n isError: false,\n isFetching: result.isFetching,\n isPlaceholderData: result.isPlaceholderData,\n fetchStatus: result.fetchStatus,\n refresh,\n }\n}\n\nexport interface UseSuspenseInfiniteQueryResult<TData, TError = Error> {\n data: TData\n error: TError | null\n status: 'success'\n isPending: false\n isSuccess: true\n isError: false\n isFetching: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n hasNextPage: boolean\n hasPreviousPage: boolean\n isFetchingNextPage: boolean\n isFetchingPreviousPage: boolean\n isFetchNextPageError: boolean\n isFetchPreviousPageError: boolean\n refresh: () => void\n fetchNextPage: () => void\n fetchPreviousPage: () => void\n}\n\n/**\n * Suspense variant of {@link useInfiniteQuery}. Same shape as `useInfiniteQuery`,\n * with `data` narrowed to non-nullable.\n */\nexport function useSuspenseInfiniteQuery<\n TData,\n TError = Error,\n TPageParam = unknown,\n>(\n query: InfiniteQueryResult<TData, TError, TPageParam>,\n): UseSuspenseInfiniteQueryResult<TData, TError> {\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n const fetchNextPage = useUnit(query.fetchNextPage)\n const fetchPreviousPage = useUnit(query.fetchPreviousPage)\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n const observer = useSuspenseObserver(query)\n\n useObserverRerender(observer)\n\n const result = observer.getOptimisticResult(observer.options as any)\n\n if (result.status === 'error') throw result.error\n if (result.status === 'pending') {\n throw (\n observer as unknown as {\n fetchOptimistic: (\n options: typeof observer.options,\n ) => Promise<unknown>\n }\n ).fetchOptimistic(observer.options)\n }\n\n const r = result as typeof result & {\n hasNextPage: boolean\n hasPreviousPage: boolean\n isFetchingNextPage: boolean\n isFetchingPreviousPage: boolean\n isFetchNextPageError: boolean\n isFetchPreviousPageError: boolean\n }\n\n return {\n data: r.data as TData,\n error: r.error as TError | null,\n status: 'success',\n isPending: false,\n isSuccess: true,\n isError: false,\n isFetching: r.isFetching,\n isPlaceholderData: r.isPlaceholderData,\n fetchStatus: r.fetchStatus,\n hasNextPage: r.hasNextPage,\n hasPreviousPage: r.hasPreviousPage,\n isFetchingNextPage: r.isFetchingNextPage,\n isFetchingPreviousPage: r.isFetchingPreviousPage,\n isFetchNextPageError: r.isFetchNextPageError,\n isFetchPreviousPageError: r.isFetchPreviousPageError,\n refresh,\n fetchNextPage,\n fetchPreviousPage,\n }\n}\n\n/**\n * Resolves a per-scope observer for suspense usage. Prefers the scope's\n * `$observer` (set by mountFx); falls back to a transient observer\n * constructed via `__createObserver` so that the very first render — before\n * useEffect has fired — has a working observer. Both flavors read/write the\n * same queryClient cache, so the transient observer is a thin wrapper.\n */\nfunction useSuspenseObserver<\n TQuery extends {\n $observer: import('effector').Store<TObserver | null>\n $queryClient: import('effector').Store<\n import('@tanstack/query-core').QueryClient | null\n >\n },\n TObserver extends {\n options: { queryKey: unknown }\n setOptions(options: any): void\n subscribe(cb: () => void): () => void\n getOptimisticResult(options: any): {\n status: 'pending' | 'success' | 'error'\n data: unknown\n error: unknown\n isFetching: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n // Infinite-query result fields — present at runtime when the underlying\n // observer is an InfiniteQueryObserver; the suspense hooks narrow as\n // needed. Typed as `any` here to keep the constraint loose.\n hasNextPage?: any\n hasPreviousPage?: any\n isFetchingNextPage?: any\n isFetchingPreviousPage?: any\n isFetchNextPageError?: any\n isFetchPreviousPageError?: any\n }\n fetchOptimistic(options: any): Promise<unknown>\n },\n>(query: TQuery): TObserver {\n const factory = query as unknown as TQuery & SuspenseFactory<TObserver>\n const observerInScope = useUnit(query.$observer) as TObserver | null\n const qc = useUnit(query.$queryClient)\n const queryKey = useUnit(factory.__resolvedKey)\n const enabled = useUnit(factory.__enabled)\n\n // Memoize a transient observer keyed by qc, so it survives across renders\n // while the scope observer is null. Once observerInScope appears, we\n // switch — the transient one is unsubscribed and abandoned (it never\n // subscribed to queryCache, so there is nothing to leak).\n const transient = React.useMemo(() => {\n if (observerInScope || !qc) return null\n return factory.__createObserver(qc, { queryKey, enabled })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [observerInScope, qc, factory])\n\n // Keep the transient observer's options in sync with reactive key/enabled,\n // so re-suspending on key changes still works through it.\n React.useEffect(() => {\n if (!transient) return\n transient.setOptions({ ...transient.options, queryKey, enabled })\n }, [transient, queryKey, enabled])\n\n const observer = observerInScope ?? transient\n if (!observer) {\n throw new Error(\n '[@effector-tanstack-query/react] useSuspenseQuery: no QueryClient is set. ' +\n 'Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] }).',\n )\n }\n return observer\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["useUnit","$queryClient","React","hydrate"],"mappings":"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","file":"index.cjs","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { useUnit } from 'effector-react'\nimport { hydrate } from '@tanstack/query-core'\nimport type {\n DehydratedState,\n FetchStatus,\n HydrateOptions,\n MutateOptions,\n QueryStatus,\n} from '@tanstack/query-core'\nimport { $queryClient } from '@effector-tanstack-query/core'\nimport type {\n InfiniteQueryResult,\n MutationResult,\n MutationStatus,\n QueryResult,\n} from '@effector-tanstack-query/core'\n\nexport interface HydrationBoundaryProps {\n /**\n * Snapshot produced by `dehydrate(queryClient)` on the server. Re-applied\n * to the scope's `QueryClient` cache so observers mounted under this tree\n * read prefetched data instead of triggering fresh network requests.\n */\n state?: DehydratedState\n /** Forwarded to `hydrate(...)` — see `@tanstack/query-core` docs. */\n options?: HydrateOptions\n children?: React.ReactNode\n}\n\n/**\n * Merges a server-prefetched `DehydratedState` into the scope's\n * `QueryClient` cache.\n *\n * Mirrors `<HydrationBoundary>` from `@tanstack/react-query`: hydration\n * runs in `useMemo` so the merge happens during the render phase (children\n * see a populated cache on their first render, no flash). The hook\n * resolves the QueryClient via `useUnit($queryClient)` instead of\n * `useQueryClient()` — meaning each fork scope can have its own client\n * without an additional `<QueryClientProvider>` in the tree.\n *\n * `hydrate` is idempotent: re-rendering with the same `state` reference\n * is a no-op. Pass new `state` references on navigation to merge fresh\n * snapshots.\n *\n * Note: this only handles the QueryClient cache layer. Effector store\n * snapshots (e.g. `serialize(scope)`) flow through your existing\n * `<Provider>` / `<EffectorNext values>` layer — orthogonal concerns.\n */\nexport function HydrationBoundary({\n state,\n options,\n children,\n}: HydrationBoundaryProps): React.ReactElement {\n const queryClient = useUnit($queryClient)\n React.useMemo(() => {\n if (queryClient && state) hydrate(queryClient, state, options)\n }, [queryClient, state, options])\n return React.createElement(React.Fragment, null, children)\n}\n\nexport interface UseQueryResult<TData, TError = Error> {\n data: TData | undefined\n error: TError | null\n status: QueryStatus\n isPending: boolean\n isFetching: boolean\n isSuccess: boolean\n isError: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n refresh: () => void\n}\n\n/**\n * Subscribes a React component to a query, automatically calling\n * `mounted()` on mount and `unmounted()` on cleanup.\n */\nexport function useQuery<TData, TError = Error>(\n query: QueryResult<TData, TError>,\n): UseQueryResult<TData, TError> {\n const state = useUnit({\n data: query.$data,\n error: query.$error,\n status: query.$status,\n isPending: query.$isPending,\n isFetching: query.$isFetching,\n isSuccess: query.$isSuccess,\n isError: query.$isError,\n isPlaceholderData: query.$isPlaceholderData,\n fetchStatus: query.$fetchStatus,\n })\n\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n return { ...state, refresh }\n}\n\nexport interface UseMutationResult<TData, TError, TVariables> {\n data: TData | undefined\n error: TError | null\n status: MutationStatus\n variables: TVariables | undefined\n isPaused: boolean\n isPending: boolean\n isSuccess: boolean\n isError: boolean\n isIdle: boolean\n mutate: (variables: TVariables) => void\n /**\n * Trigger the mutation with per-call callbacks layered on top of the\n * observer-level ones (`onSuccess` / `onError` / `onSettled` in\n * `createMutation` options). Use this for component-local reactions\n * that don't fit module-level `sample` wiring — navigation after success,\n * one-shot toasts, etc.\n */\n mutateWith: (args: {\n variables: TVariables\n onSuccess?: MutateOptions<TData, TError, TVariables>['onSuccess']\n onError?: MutateOptions<TData, TError, TVariables>['onError']\n onSettled?: MutateOptions<TData, TError, TVariables>['onSettled']\n }) => void\n reset: () => void\n}\n\n/**\n * Subscribes a React component to a mutation, automatically calling\n * `start()` on mount and `unmounted()` on cleanup so the queryClient can\n * garbage-collect the mutation entry once no observers remain.\n */\nexport function useMutation<TData = unknown, TError = Error, TVariables = void>(\n mutation: MutationResult<TData, TError, TVariables>,\n): UseMutationResult<TData, TError, TVariables> {\n const state = useUnit({\n data: mutation.$data,\n error: mutation.$error,\n status: mutation.$status,\n variables: mutation.$variables,\n isPaused: mutation.$isPaused,\n isPending: mutation.$isPending,\n isSuccess: mutation.$isSuccess,\n isError: mutation.$isError,\n isIdle: mutation.$isIdle,\n })\n\n const start = useUnit(mutation.start)\n const unmount = useUnit(mutation.unmounted)\n const mutate = useUnit(mutation.mutate)\n const mutateWith = useUnit(mutation.mutateWith)\n const reset = useUnit(mutation.reset)\n\n React.useEffect(() => {\n start()\n return () => unmount()\n }, [start, unmount])\n\n return { ...state, mutate, mutateWith, reset }\n}\n\nexport interface UseInfiniteQueryResult<TData, TError> {\n data: TData | undefined\n error: TError | null\n status: QueryStatus\n isPending: boolean\n isFetching: boolean\n isSuccess: boolean\n isError: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n hasNextPage: boolean\n hasPreviousPage: boolean\n isFetchingNextPage: boolean\n isFetchingPreviousPage: boolean\n isFetchNextPageError: boolean\n isFetchPreviousPageError: boolean\n refresh: () => void\n fetchNextPage: () => void\n fetchPreviousPage: () => void\n}\n\n/**\n * Subscribes a React component to an infinite query, with auto mount/unmount\n * lifecycle and bound `fetchNextPage` / `fetchPreviousPage` callbacks.\n */\nexport function useInfiniteQuery<TData, TError = Error, TPageParam = unknown>(\n query: InfiniteQueryResult<TData, TError, TPageParam>,\n): UseInfiniteQueryResult<TData, TError> {\n const state = useUnit({\n data: query.$data,\n error: query.$error,\n status: query.$status,\n isPending: query.$isPending,\n isFetching: query.$isFetching,\n isSuccess: query.$isSuccess,\n isError: query.$isError,\n isPlaceholderData: query.$isPlaceholderData,\n fetchStatus: query.$fetchStatus,\n hasNextPage: query.$hasNextPage,\n hasPreviousPage: query.$hasPreviousPage,\n isFetchingNextPage: query.$isFetchingNextPage,\n isFetchingPreviousPage: query.$isFetchingPreviousPage,\n isFetchNextPageError: query.$isFetchNextPageError,\n isFetchPreviousPageError: query.$isFetchPreviousPageError,\n })\n\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n const fetchNextPage = useUnit(query.fetchNextPage)\n const fetchPreviousPage = useUnit(query.fetchPreviousPage)\n\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n return { ...state, refresh, fetchNextPage, fetchPreviousPage }\n}\n\n// Suspense data path: read from a per-scope observer.\n//\n// The scope mount chain runs in useEffect, which is skipped while a component\n// is suspended — so on the very first render the scope's `$observer` may be\n// null. To get synchronous access to the observer's promise during suspense,\n// we construct a transient observer via the factory's hidden\n// `__createObserver(qc, { queryKey, enabled })` helper. The transient observer\n// reads from / writes to the same queryClient cache as the eventual scope\n// observer (which is created when mountFx runs after useEffect commits).\n//\n// The mount/unmount effect is still wired up so that other consumers reading\n// the same query through `useUnit` / `useQuery` see updates in scope state.\n\nfunction useObserverRerender(\n observer: { subscribe: (cb: () => void) => () => void } | null,\n): void {\n const [, forceRender] = React.useReducer((x: number) => x + 1, 0)\n React.useEffect(() => {\n if (!observer) return\n return observer.subscribe(forceRender)\n }, [observer])\n}\n\ninterface SuspenseFactory<TObserver> {\n __createObserver(\n qc: import('@tanstack/query-core').QueryClient,\n init: { queryKey: unknown; enabled: boolean },\n ): TObserver\n __resolvedKey: import('effector').Store<unknown>\n __enabled: import('effector').Store<boolean>\n}\n\nexport interface UseSuspenseQueryResult<TData, TError = Error> {\n /** Resolved query data — non-nullable inside the rendered subtree (Suspense\n * absorbed the pending state). */\n data: TData\n /** Always `null` past the Suspense gate; errors are thrown to the nearest\n * `<ErrorBoundary>`. Typed as `TError | null` for consistency with\n * `useQuery` so the same destructure works in both. */\n error: TError | null\n status: 'success'\n isPending: false\n isSuccess: true\n isError: false\n /** `true` while a background refetch is running. Use for refresh spinners. */\n isFetching: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n refresh: () => void\n}\n\n/**\n * Reads a query for use inside a `<Suspense>` boundary. While the query is\n * pending, throws an inflight promise (queryClient-deduplicated). On error,\n * throws the error — catch with `<ErrorBoundary>`. Returns the same shape as\n * `useQuery`, but with `data` narrowed to non-nullable `TData` since the\n * pending state is impossible past the Suspense gate.\n */\nexport function useSuspenseQuery<TData, TError = Error>(\n query: QueryResult<TData, TError>,\n): UseSuspenseQueryResult<TData, TError> {\n // Auto-mount lifecycle so concurrent consumers (useUnit / useQuery) reading\n // the same query through the effector scope stay in sync.\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n const observer = useSuspenseObserver(query)\n useObserverRerender(observer)\n\n // Store snapshot — always read so hook order is fixed across renders.\n // The observer path doesn't use it; the store-only path (server-RSC of\n // a scope rehydrated from `serialize`, where `$observer` and\n // `$queryClient` are both `null` because they're `serialize: 'ignore'`)\n // reads everything from here.\n const state = useUnit({\n data: query.$data,\n error: query.$error,\n status: query.$status,\n isFetching: query.$isFetching,\n isPlaceholderData: query.$isPlaceholderData,\n fetchStatus: query.$fetchStatus,\n })\n\n // Observer path: `getOptimisticResult` reads from `QueryCache`\n // synchronously and reflects fetches/refetches the moment the observer\n // notifies. Used whenever we have an in-scope observer\n // (post-`mounted()`) or a transient one built from `$queryClient`.\n if (observer) {\n const result = observer.getOptimisticResult(observer.options as any)\n\n if (result.status === 'error') throw result.error\n if (result.status === 'pending') {\n throw observer.fetchOptimistic(observer.options as any)\n }\n\n return {\n data: result.data as TData,\n error: result.error as TError | null,\n status: 'success',\n isPending: false,\n isSuccess: true,\n isError: false,\n isFetching: result.isFetching,\n isPlaceholderData: result.isPlaceholderData,\n fetchStatus: result.fetchStatus,\n refresh,\n }\n }\n\n // Store-only path: no observer materialisable. Trust `$status` —\n // populated by `prefetchQueries` before the scope was serialised. A\n // pending status here means there's no `QueryClient` and no prefetch\n // happened: can't deduplicate-throw a fetch promise, so surface the\n // misconfiguration as an error to `<ErrorBoundary>`.\n if (state.status === 'error') throw state.error\n if (state.status === 'pending') {\n throw new Error(\n '[@effector-tanstack-query/react] useSuspenseQuery: no QueryClient is set. ' +\n 'Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] }).',\n )\n }\n\n return {\n data: state.data as TData,\n error: state.error as TError | null,\n status: 'success',\n isPending: false,\n isSuccess: true,\n isError: false,\n isFetching: state.isFetching,\n isPlaceholderData: state.isPlaceholderData,\n fetchStatus: state.fetchStatus,\n refresh,\n }\n}\n\nexport interface UseSuspenseInfiniteQueryResult<TData, TError = Error> {\n data: TData\n error: TError | null\n status: 'success'\n isPending: false\n isSuccess: true\n isError: false\n isFetching: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n hasNextPage: boolean\n hasPreviousPage: boolean\n isFetchingNextPage: boolean\n isFetchingPreviousPage: boolean\n isFetchNextPageError: boolean\n isFetchPreviousPageError: boolean\n refresh: () => void\n fetchNextPage: () => void\n fetchPreviousPage: () => void\n}\n\n/**\n * Suspense variant of {@link useInfiniteQuery}. Same shape as `useInfiniteQuery`,\n * with `data` narrowed to non-nullable.\n */\nexport function useSuspenseInfiniteQuery<\n TData,\n TError = Error,\n TPageParam = unknown,\n>(\n query: InfiniteQueryResult<TData, TError, TPageParam>,\n): UseSuspenseInfiniteQueryResult<TData, TError> {\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n const fetchNextPage = useUnit(query.fetchNextPage)\n const fetchPreviousPage = useUnit(query.fetchPreviousPage)\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n const observer = useSuspenseObserver(query)\n useObserverRerender(observer)\n\n // See `useSuspenseQuery` for the observer-vs-stores dual path rationale.\n // Infinite queries carry pagination fields in separate effector stores\n // (`$hasNextPage`, `$isFetchingNextPage`, …), so the store snapshot has\n // to read those too.\n const state = useUnit({\n data: query.$data,\n error: query.$error,\n status: query.$status,\n isFetching: query.$isFetching,\n isPlaceholderData: query.$isPlaceholderData,\n fetchStatus: query.$fetchStatus,\n hasNextPage: query.$hasNextPage,\n hasPreviousPage: query.$hasPreviousPage,\n isFetchingNextPage: query.$isFetchingNextPage,\n isFetchingPreviousPage: query.$isFetchingPreviousPage,\n isFetchNextPageError: query.$isFetchNextPageError,\n isFetchPreviousPageError: query.$isFetchPreviousPageError,\n })\n\n if (observer) {\n const obs = observer\n const result = obs.getOptimisticResult(obs.options as any)\n\n if (result.status === 'error') throw result.error\n if (result.status === 'pending') {\n throw (\n obs as unknown as {\n fetchOptimistic: (options: typeof obs.options) => Promise<unknown>\n }\n ).fetchOptimistic(obs.options)\n }\n\n const r = result as typeof result & {\n hasNextPage: boolean\n hasPreviousPage: boolean\n isFetchingNextPage: boolean\n isFetchingPreviousPage: boolean\n isFetchNextPageError: boolean\n isFetchPreviousPageError: boolean\n }\n\n return {\n data: r.data as TData,\n error: r.error as TError | null,\n status: 'success',\n isPending: false,\n isSuccess: true,\n isError: false,\n isFetching: r.isFetching,\n isPlaceholderData: r.isPlaceholderData,\n fetchStatus: r.fetchStatus,\n hasNextPage: r.hasNextPage,\n hasPreviousPage: r.hasPreviousPage,\n isFetchingNextPage: r.isFetchingNextPage,\n isFetchingPreviousPage: r.isFetchingPreviousPage,\n isFetchNextPageError: r.isFetchNextPageError,\n isFetchPreviousPageError: r.isFetchPreviousPageError,\n refresh,\n fetchNextPage,\n fetchPreviousPage,\n }\n }\n\n // Store-only path (server-RSC of a hydrated scope).\n if (state.status === 'error') throw state.error\n if (state.status === 'pending') {\n throw new Error(\n '[@effector-tanstack-query/react] useSuspenseInfiniteQuery: no QueryClient is set. ' +\n 'Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] }).',\n )\n }\n\n return {\n data: state.data as TData,\n error: state.error as TError | null,\n status: 'success',\n isPending: false,\n isSuccess: true,\n isError: false,\n isFetching: state.isFetching,\n isPlaceholderData: state.isPlaceholderData,\n fetchStatus: state.fetchStatus,\n hasNextPage: state.hasNextPage,\n hasPreviousPage: state.hasPreviousPage,\n isFetchingNextPage: state.isFetchingNextPage,\n isFetchingPreviousPage: state.isFetchingPreviousPage,\n isFetchNextPageError: state.isFetchNextPageError,\n isFetchPreviousPageError: state.isFetchPreviousPageError,\n refresh,\n fetchNextPage,\n fetchPreviousPage,\n }\n}\n\n/**\n * Resolves a per-scope observer for suspense usage. Prefers the scope's\n * `$observer` (set by mountFx); falls back to a transient observer\n * constructed via `__createObserver` so that the very first render — before\n * useEffect has fired — has a working observer. Both flavors read/write the\n * same queryClient cache, so the transient observer is a thin wrapper.\n */\nfunction useSuspenseObserver<\n TQuery extends {\n $observer: import('effector').Store<TObserver | null>\n $queryClient: import('effector').Store<\n import('@tanstack/query-core').QueryClient | null\n >\n },\n TObserver extends {\n options: { queryKey: unknown }\n setOptions(options: any): void\n subscribe(cb: () => void): () => void\n getOptimisticResult(options: any): {\n status: 'pending' | 'success' | 'error'\n data: unknown\n error: unknown\n isFetching: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n // Infinite-query result fields — present at runtime when the underlying\n // observer is an InfiniteQueryObserver; the suspense hooks narrow as\n // needed. Typed as `any` here to keep the constraint loose.\n hasNextPage?: any\n hasPreviousPage?: any\n isFetchingNextPage?: any\n isFetchingPreviousPage?: any\n isFetchNextPageError?: any\n isFetchPreviousPageError?: any\n }\n fetchOptimistic(options: any): Promise<unknown>\n },\n>(query: TQuery): TObserver | null {\n const factory = query as unknown as TQuery & SuspenseFactory<TObserver>\n const observerInScope = useUnit(query.$observer) as TObserver | null\n const qc = useUnit(query.$queryClient)\n const queryKey = useUnit(factory.__resolvedKey)\n const enabled = useUnit(factory.__enabled)\n\n // Memoize a transient observer keyed by qc, so it survives across renders\n // while the scope observer is null. Once observerInScope appears, we\n // switch — the transient one is unsubscribed and abandoned (it never\n // subscribed to queryCache, so there is nothing to leak).\n const transient = React.useMemo(() => {\n if (observerInScope || !qc) return null\n return factory.__createObserver(qc, { queryKey, enabled })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [observerInScope, qc, factory])\n\n // Keep the transient observer's options in sync with reactive key/enabled,\n // so re-suspending on key changes still works through it.\n React.useEffect(() => {\n if (!transient) return\n transient.setOptions({ ...transient.options, queryKey, enabled })\n }, [transient, queryKey, enabled])\n\n // Null is a legitimate return: server-RSC render of a scope built from\n // `serialize(scope)` has neither `$observer` nor `$queryClient` (both are\n // `serialize: 'ignore'` — instances can't ride through the RSC boundary).\n // Callers branch on `$status === 'success'` from the serialized stores;\n // they only error out when a pending state is unreachable without an\n // observer to throw `fetchOptimistic` on.\n return observerInScope ?? transient\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -90,7 +90,10 @@ function useInfiniteQuery(query) {
|
|
|
90
90
|
}
|
|
91
91
|
function useObserverRerender(observer) {
|
|
92
92
|
const [, forceRender] = React.useReducer((x) => x + 1, 0);
|
|
93
|
-
React.useEffect(() =>
|
|
93
|
+
React.useEffect(() => {
|
|
94
|
+
if (!observer) return;
|
|
95
|
+
return observer.subscribe(forceRender);
|
|
96
|
+
}, [observer]);
|
|
94
97
|
}
|
|
95
98
|
function useSuspenseQuery(query) {
|
|
96
99
|
const mount = useUnit(query.mounted);
|
|
@@ -102,21 +105,49 @@ function useSuspenseQuery(query) {
|
|
|
102
105
|
}, [mount, unmount]);
|
|
103
106
|
const observer = useSuspenseObserver(query);
|
|
104
107
|
useObserverRerender(observer);
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
const state = useUnit({
|
|
109
|
+
data: query.$data,
|
|
110
|
+
error: query.$error,
|
|
111
|
+
status: query.$status,
|
|
112
|
+
isFetching: query.$isFetching,
|
|
113
|
+
isPlaceholderData: query.$isPlaceholderData,
|
|
114
|
+
fetchStatus: query.$fetchStatus
|
|
115
|
+
});
|
|
116
|
+
if (observer) {
|
|
117
|
+
const result = observer.getOptimisticResult(observer.options);
|
|
118
|
+
if (result.status === "error") throw result.error;
|
|
119
|
+
if (result.status === "pending") {
|
|
120
|
+
throw observer.fetchOptimistic(observer.options);
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
data: result.data,
|
|
124
|
+
error: result.error,
|
|
125
|
+
status: "success",
|
|
126
|
+
isPending: false,
|
|
127
|
+
isSuccess: true,
|
|
128
|
+
isError: false,
|
|
129
|
+
isFetching: result.isFetching,
|
|
130
|
+
isPlaceholderData: result.isPlaceholderData,
|
|
131
|
+
fetchStatus: result.fetchStatus,
|
|
132
|
+
refresh
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (state.status === "error") throw state.error;
|
|
136
|
+
if (state.status === "pending") {
|
|
137
|
+
throw new Error(
|
|
138
|
+
"[@effector-tanstack-query/react] useSuspenseQuery: no QueryClient is set. Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] })."
|
|
139
|
+
);
|
|
109
140
|
}
|
|
110
141
|
return {
|
|
111
|
-
data:
|
|
112
|
-
error:
|
|
142
|
+
data: state.data,
|
|
143
|
+
error: state.error,
|
|
113
144
|
status: "success",
|
|
114
145
|
isPending: false,
|
|
115
146
|
isSuccess: true,
|
|
116
147
|
isError: false,
|
|
117
|
-
isFetching:
|
|
118
|
-
isPlaceholderData:
|
|
119
|
-
fetchStatus:
|
|
148
|
+
isFetching: state.isFetching,
|
|
149
|
+
isPlaceholderData: state.isPlaceholderData,
|
|
150
|
+
fetchStatus: state.fetchStatus,
|
|
120
151
|
refresh
|
|
121
152
|
};
|
|
122
153
|
}
|
|
@@ -132,28 +163,71 @@ function useSuspenseInfiniteQuery(query) {
|
|
|
132
163
|
}, [mount, unmount]);
|
|
133
164
|
const observer = useSuspenseObserver(query);
|
|
134
165
|
useObserverRerender(observer);
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
166
|
+
const state = useUnit({
|
|
167
|
+
data: query.$data,
|
|
168
|
+
error: query.$error,
|
|
169
|
+
status: query.$status,
|
|
170
|
+
isFetching: query.$isFetching,
|
|
171
|
+
isPlaceholderData: query.$isPlaceholderData,
|
|
172
|
+
fetchStatus: query.$fetchStatus,
|
|
173
|
+
hasNextPage: query.$hasNextPage,
|
|
174
|
+
hasPreviousPage: query.$hasPreviousPage,
|
|
175
|
+
isFetchingNextPage: query.$isFetchingNextPage,
|
|
176
|
+
isFetchingPreviousPage: query.$isFetchingPreviousPage,
|
|
177
|
+
isFetchNextPageError: query.$isFetchNextPageError,
|
|
178
|
+
isFetchPreviousPageError: query.$isFetchPreviousPageError
|
|
179
|
+
});
|
|
180
|
+
if (observer) {
|
|
181
|
+
const obs = observer;
|
|
182
|
+
const result = obs.getOptimisticResult(obs.options);
|
|
183
|
+
if (result.status === "error") throw result.error;
|
|
184
|
+
if (result.status === "pending") {
|
|
185
|
+
throw obs.fetchOptimistic(obs.options);
|
|
186
|
+
}
|
|
187
|
+
const r = result;
|
|
188
|
+
return {
|
|
189
|
+
data: r.data,
|
|
190
|
+
error: r.error,
|
|
191
|
+
status: "success",
|
|
192
|
+
isPending: false,
|
|
193
|
+
isSuccess: true,
|
|
194
|
+
isError: false,
|
|
195
|
+
isFetching: r.isFetching,
|
|
196
|
+
isPlaceholderData: r.isPlaceholderData,
|
|
197
|
+
fetchStatus: r.fetchStatus,
|
|
198
|
+
hasNextPage: r.hasNextPage,
|
|
199
|
+
hasPreviousPage: r.hasPreviousPage,
|
|
200
|
+
isFetchingNextPage: r.isFetchingNextPage,
|
|
201
|
+
isFetchingPreviousPage: r.isFetchingPreviousPage,
|
|
202
|
+
isFetchNextPageError: r.isFetchNextPageError,
|
|
203
|
+
isFetchPreviousPageError: r.isFetchPreviousPageError,
|
|
204
|
+
refresh,
|
|
205
|
+
fetchNextPage,
|
|
206
|
+
fetchPreviousPage
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
if (state.status === "error") throw state.error;
|
|
210
|
+
if (state.status === "pending") {
|
|
211
|
+
throw new Error(
|
|
212
|
+
"[@effector-tanstack-query/react] useSuspenseInfiniteQuery: no QueryClient is set. Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] })."
|
|
213
|
+
);
|
|
139
214
|
}
|
|
140
|
-
const r = result;
|
|
141
215
|
return {
|
|
142
|
-
data:
|
|
143
|
-
error:
|
|
216
|
+
data: state.data,
|
|
217
|
+
error: state.error,
|
|
144
218
|
status: "success",
|
|
145
219
|
isPending: false,
|
|
146
220
|
isSuccess: true,
|
|
147
221
|
isError: false,
|
|
148
|
-
isFetching:
|
|
149
|
-
isPlaceholderData:
|
|
150
|
-
fetchStatus:
|
|
151
|
-
hasNextPage:
|
|
152
|
-
hasPreviousPage:
|
|
153
|
-
isFetchingNextPage:
|
|
154
|
-
isFetchingPreviousPage:
|
|
155
|
-
isFetchNextPageError:
|
|
156
|
-
isFetchPreviousPageError:
|
|
222
|
+
isFetching: state.isFetching,
|
|
223
|
+
isPlaceholderData: state.isPlaceholderData,
|
|
224
|
+
fetchStatus: state.fetchStatus,
|
|
225
|
+
hasNextPage: state.hasNextPage,
|
|
226
|
+
hasPreviousPage: state.hasPreviousPage,
|
|
227
|
+
isFetchingNextPage: state.isFetchingNextPage,
|
|
228
|
+
isFetchingPreviousPage: state.isFetchingPreviousPage,
|
|
229
|
+
isFetchNextPageError: state.isFetchNextPageError,
|
|
230
|
+
isFetchPreviousPageError: state.isFetchPreviousPageError,
|
|
157
231
|
refresh,
|
|
158
232
|
fetchNextPage,
|
|
159
233
|
fetchPreviousPage
|
|
@@ -173,13 +247,7 @@ function useSuspenseObserver(query) {
|
|
|
173
247
|
if (!transient) return;
|
|
174
248
|
transient.setOptions({ ...transient.options, queryKey, enabled });
|
|
175
249
|
}, [transient, queryKey, enabled]);
|
|
176
|
-
|
|
177
|
-
if (!observer) {
|
|
178
|
-
throw new Error(
|
|
179
|
-
"[@effector-tanstack-query/react] useSuspenseQuery: no QueryClient is set. Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] })."
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
return observer;
|
|
250
|
+
return observerInScope ?? transient;
|
|
183
251
|
}
|
|
184
252
|
|
|
185
253
|
export { HydrationBoundary, useInfiniteQuery, useMutation, useQuery, useSuspenseInfiniteQuery, useSuspenseQuery };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { useUnit } from 'effector-react'\nimport { hydrate } from '@tanstack/query-core'\nimport type {\n DehydratedState,\n FetchStatus,\n HydrateOptions,\n MutateOptions,\n QueryStatus,\n} from '@tanstack/query-core'\nimport { $queryClient } from '@effector-tanstack-query/core'\nimport type {\n InfiniteQueryResult,\n MutationResult,\n MutationStatus,\n QueryResult,\n} from '@effector-tanstack-query/core'\n\nexport interface HydrationBoundaryProps {\n /**\n * Snapshot produced by `dehydrate(queryClient)` on the server. Re-applied\n * to the scope's `QueryClient` cache so observers mounted under this tree\n * read prefetched data instead of triggering fresh network requests.\n */\n state?: DehydratedState\n /** Forwarded to `hydrate(...)` — see `@tanstack/query-core` docs. */\n options?: HydrateOptions\n children?: React.ReactNode\n}\n\n/**\n * Merges a server-prefetched `DehydratedState` into the scope's\n * `QueryClient` cache.\n *\n * Mirrors `<HydrationBoundary>` from `@tanstack/react-query`: hydration\n * runs in `useMemo` so the merge happens during the render phase (children\n * see a populated cache on their first render, no flash). The hook\n * resolves the QueryClient via `useUnit($queryClient)` instead of\n * `useQueryClient()` — meaning each fork scope can have its own client\n * without an additional `<QueryClientProvider>` in the tree.\n *\n * `hydrate` is idempotent: re-rendering with the same `state` reference\n * is a no-op. Pass new `state` references on navigation to merge fresh\n * snapshots.\n *\n * Note: this only handles the QueryClient cache layer. Effector store\n * snapshots (e.g. `serialize(scope)`) flow through your existing\n * `<Provider>` / `<EffectorNext values>` layer — orthogonal concerns.\n */\nexport function HydrationBoundary({\n state,\n options,\n children,\n}: HydrationBoundaryProps): React.ReactElement {\n const queryClient = useUnit($queryClient)\n React.useMemo(() => {\n if (queryClient && state) hydrate(queryClient, state, options)\n }, [queryClient, state, options])\n return React.createElement(React.Fragment, null, children)\n}\n\nexport interface UseQueryResult<TData, TError = Error> {\n data: TData | undefined\n error: TError | null\n status: QueryStatus\n isPending: boolean\n isFetching: boolean\n isSuccess: boolean\n isError: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n refresh: () => void\n}\n\n/**\n * Subscribes a React component to a query, automatically calling\n * `mounted()` on mount and `unmounted()` on cleanup.\n */\nexport function useQuery<TData, TError = Error>(\n query: QueryResult<TData, TError>,\n): UseQueryResult<TData, TError> {\n const state = useUnit({\n data: query.$data,\n error: query.$error,\n status: query.$status,\n isPending: query.$isPending,\n isFetching: query.$isFetching,\n isSuccess: query.$isSuccess,\n isError: query.$isError,\n isPlaceholderData: query.$isPlaceholderData,\n fetchStatus: query.$fetchStatus,\n })\n\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n return { ...state, refresh }\n}\n\nexport interface UseMutationResult<TData, TError, TVariables> {\n data: TData | undefined\n error: TError | null\n status: MutationStatus\n variables: TVariables | undefined\n isPaused: boolean\n isPending: boolean\n isSuccess: boolean\n isError: boolean\n isIdle: boolean\n mutate: (variables: TVariables) => void\n /**\n * Trigger the mutation with per-call callbacks layered on top of the\n * observer-level ones (`onSuccess` / `onError` / `onSettled` in\n * `createMutation` options). Use this for component-local reactions\n * that don't fit module-level `sample` wiring — navigation after success,\n * one-shot toasts, etc.\n */\n mutateWith: (args: {\n variables: TVariables\n onSuccess?: MutateOptions<TData, TError, TVariables>['onSuccess']\n onError?: MutateOptions<TData, TError, TVariables>['onError']\n onSettled?: MutateOptions<TData, TError, TVariables>['onSettled']\n }) => void\n reset: () => void\n}\n\n/**\n * Subscribes a React component to a mutation, automatically calling\n * `start()` on mount and `unmounted()` on cleanup so the queryClient can\n * garbage-collect the mutation entry once no observers remain.\n */\nexport function useMutation<TData = unknown, TError = Error, TVariables = void>(\n mutation: MutationResult<TData, TError, TVariables>,\n): UseMutationResult<TData, TError, TVariables> {\n const state = useUnit({\n data: mutation.$data,\n error: mutation.$error,\n status: mutation.$status,\n variables: mutation.$variables,\n isPaused: mutation.$isPaused,\n isPending: mutation.$isPending,\n isSuccess: mutation.$isSuccess,\n isError: mutation.$isError,\n isIdle: mutation.$isIdle,\n })\n\n const start = useUnit(mutation.start)\n const unmount = useUnit(mutation.unmounted)\n const mutate = useUnit(mutation.mutate)\n const mutateWith = useUnit(mutation.mutateWith)\n const reset = useUnit(mutation.reset)\n\n React.useEffect(() => {\n start()\n return () => unmount()\n }, [start, unmount])\n\n return { ...state, mutate, mutateWith, reset }\n}\n\nexport interface UseInfiniteQueryResult<TData, TError> {\n data: TData | undefined\n error: TError | null\n status: QueryStatus\n isPending: boolean\n isFetching: boolean\n isSuccess: boolean\n isError: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n hasNextPage: boolean\n hasPreviousPage: boolean\n isFetchingNextPage: boolean\n isFetchingPreviousPage: boolean\n isFetchNextPageError: boolean\n isFetchPreviousPageError: boolean\n refresh: () => void\n fetchNextPage: () => void\n fetchPreviousPage: () => void\n}\n\n/**\n * Subscribes a React component to an infinite query, with auto mount/unmount\n * lifecycle and bound `fetchNextPage` / `fetchPreviousPage` callbacks.\n */\nexport function useInfiniteQuery<TData, TError = Error, TPageParam = unknown>(\n query: InfiniteQueryResult<TData, TError, TPageParam>,\n): UseInfiniteQueryResult<TData, TError> {\n const state = useUnit({\n data: query.$data,\n error: query.$error,\n status: query.$status,\n isPending: query.$isPending,\n isFetching: query.$isFetching,\n isSuccess: query.$isSuccess,\n isError: query.$isError,\n isPlaceholderData: query.$isPlaceholderData,\n fetchStatus: query.$fetchStatus,\n hasNextPage: query.$hasNextPage,\n hasPreviousPage: query.$hasPreviousPage,\n isFetchingNextPage: query.$isFetchingNextPage,\n isFetchingPreviousPage: query.$isFetchingPreviousPage,\n isFetchNextPageError: query.$isFetchNextPageError,\n isFetchPreviousPageError: query.$isFetchPreviousPageError,\n })\n\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n const fetchNextPage = useUnit(query.fetchNextPage)\n const fetchPreviousPage = useUnit(query.fetchPreviousPage)\n\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n return { ...state, refresh, fetchNextPage, fetchPreviousPage }\n}\n\n// Suspense data path: read from a per-scope observer.\n//\n// The scope mount chain runs in useEffect, which is skipped while a component\n// is suspended — so on the very first render the scope's `$observer` may be\n// null. To get synchronous access to the observer's promise during suspense,\n// we construct a transient observer via the factory's hidden\n// `__createObserver(qc, { queryKey, enabled })` helper. The transient observer\n// reads from / writes to the same queryClient cache as the eventual scope\n// observer (which is created when mountFx runs after useEffect commits).\n//\n// The mount/unmount effect is still wired up so that other consumers reading\n// the same query through `useUnit` / `useQuery` see updates in scope state.\n\nfunction useObserverRerender(observer: {\n subscribe: (cb: () => void) => () => void\n}): void {\n const [, forceRender] = React.useReducer((x: number) => x + 1, 0)\n React.useEffect(() => observer.subscribe(forceRender), [observer])\n}\n\ninterface SuspenseFactory<TObserver> {\n __createObserver(\n qc: import('@tanstack/query-core').QueryClient,\n init: { queryKey: unknown; enabled: boolean },\n ): TObserver\n __resolvedKey: import('effector').Store<unknown>\n __enabled: import('effector').Store<boolean>\n}\n\nexport interface UseSuspenseQueryResult<TData, TError = Error> {\n /** Resolved query data — non-nullable inside the rendered subtree (Suspense\n * absorbed the pending state). */\n data: TData\n /** Always `null` past the Suspense gate; errors are thrown to the nearest\n * `<ErrorBoundary>`. Typed as `TError | null` for consistency with\n * `useQuery` so the same destructure works in both. */\n error: TError | null\n status: 'success'\n isPending: false\n isSuccess: true\n isError: false\n /** `true` while a background refetch is running. Use for refresh spinners. */\n isFetching: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n refresh: () => void\n}\n\n/**\n * Reads a query for use inside a `<Suspense>` boundary. While the query is\n * pending, throws an inflight promise (queryClient-deduplicated). On error,\n * throws the error — catch with `<ErrorBoundary>`. Returns the same shape as\n * `useQuery`, but with `data` narrowed to non-nullable `TData` since the\n * pending state is impossible past the Suspense gate.\n */\nexport function useSuspenseQuery<TData, TError = Error>(\n query: QueryResult<TData, TError>,\n): UseSuspenseQueryResult<TData, TError> {\n // Auto-mount lifecycle so concurrent consumers (useUnit / useQuery) reading\n // the same query through the effector scope stay in sync.\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n const observer = useSuspenseObserver(query)\n\n useObserverRerender(observer)\n\n const result = observer.getOptimisticResult(observer.options as any)\n\n if (result.status === 'error') throw result.error\n if (result.status === 'pending') {\n throw observer.fetchOptimistic(observer.options as any)\n }\n\n // Read all secondary fields from the observer result, not the effector\n // stores: stores are only populated after mountFx fires from useEffect,\n // which on the very first successful render hasn't run yet. The observer\n // result is always live and consistent.\n return {\n data: result.data as TData,\n error: result.error as TError | null,\n status: 'success',\n isPending: false,\n isSuccess: true,\n isError: false,\n isFetching: result.isFetching,\n isPlaceholderData: result.isPlaceholderData,\n fetchStatus: result.fetchStatus,\n refresh,\n }\n}\n\nexport interface UseSuspenseInfiniteQueryResult<TData, TError = Error> {\n data: TData\n error: TError | null\n status: 'success'\n isPending: false\n isSuccess: true\n isError: false\n isFetching: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n hasNextPage: boolean\n hasPreviousPage: boolean\n isFetchingNextPage: boolean\n isFetchingPreviousPage: boolean\n isFetchNextPageError: boolean\n isFetchPreviousPageError: boolean\n refresh: () => void\n fetchNextPage: () => void\n fetchPreviousPage: () => void\n}\n\n/**\n * Suspense variant of {@link useInfiniteQuery}. Same shape as `useInfiniteQuery`,\n * with `data` narrowed to non-nullable.\n */\nexport function useSuspenseInfiniteQuery<\n TData,\n TError = Error,\n TPageParam = unknown,\n>(\n query: InfiniteQueryResult<TData, TError, TPageParam>,\n): UseSuspenseInfiniteQueryResult<TData, TError> {\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n const fetchNextPage = useUnit(query.fetchNextPage)\n const fetchPreviousPage = useUnit(query.fetchPreviousPage)\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n const observer = useSuspenseObserver(query)\n\n useObserverRerender(observer)\n\n const result = observer.getOptimisticResult(observer.options as any)\n\n if (result.status === 'error') throw result.error\n if (result.status === 'pending') {\n throw (\n observer as unknown as {\n fetchOptimistic: (\n options: typeof observer.options,\n ) => Promise<unknown>\n }\n ).fetchOptimistic(observer.options)\n }\n\n const r = result as typeof result & {\n hasNextPage: boolean\n hasPreviousPage: boolean\n isFetchingNextPage: boolean\n isFetchingPreviousPage: boolean\n isFetchNextPageError: boolean\n isFetchPreviousPageError: boolean\n }\n\n return {\n data: r.data as TData,\n error: r.error as TError | null,\n status: 'success',\n isPending: false,\n isSuccess: true,\n isError: false,\n isFetching: r.isFetching,\n isPlaceholderData: r.isPlaceholderData,\n fetchStatus: r.fetchStatus,\n hasNextPage: r.hasNextPage,\n hasPreviousPage: r.hasPreviousPage,\n isFetchingNextPage: r.isFetchingNextPage,\n isFetchingPreviousPage: r.isFetchingPreviousPage,\n isFetchNextPageError: r.isFetchNextPageError,\n isFetchPreviousPageError: r.isFetchPreviousPageError,\n refresh,\n fetchNextPage,\n fetchPreviousPage,\n }\n}\n\n/**\n * Resolves a per-scope observer for suspense usage. Prefers the scope's\n * `$observer` (set by mountFx); falls back to a transient observer\n * constructed via `__createObserver` so that the very first render — before\n * useEffect has fired — has a working observer. Both flavors read/write the\n * same queryClient cache, so the transient observer is a thin wrapper.\n */\nfunction useSuspenseObserver<\n TQuery extends {\n $observer: import('effector').Store<TObserver | null>\n $queryClient: import('effector').Store<\n import('@tanstack/query-core').QueryClient | null\n >\n },\n TObserver extends {\n options: { queryKey: unknown }\n setOptions(options: any): void\n subscribe(cb: () => void): () => void\n getOptimisticResult(options: any): {\n status: 'pending' | 'success' | 'error'\n data: unknown\n error: unknown\n isFetching: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n // Infinite-query result fields — present at runtime when the underlying\n // observer is an InfiniteQueryObserver; the suspense hooks narrow as\n // needed. Typed as `any` here to keep the constraint loose.\n hasNextPage?: any\n hasPreviousPage?: any\n isFetchingNextPage?: any\n isFetchingPreviousPage?: any\n isFetchNextPageError?: any\n isFetchPreviousPageError?: any\n }\n fetchOptimistic(options: any): Promise<unknown>\n },\n>(query: TQuery): TObserver {\n const factory = query as unknown as TQuery & SuspenseFactory<TObserver>\n const observerInScope = useUnit(query.$observer) as TObserver | null\n const qc = useUnit(query.$queryClient)\n const queryKey = useUnit(factory.__resolvedKey)\n const enabled = useUnit(factory.__enabled)\n\n // Memoize a transient observer keyed by qc, so it survives across renders\n // while the scope observer is null. Once observerInScope appears, we\n // switch — the transient one is unsubscribed and abandoned (it never\n // subscribed to queryCache, so there is nothing to leak).\n const transient = React.useMemo(() => {\n if (observerInScope || !qc) return null\n return factory.__createObserver(qc, { queryKey, enabled })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [observerInScope, qc, factory])\n\n // Keep the transient observer's options in sync with reactive key/enabled,\n // so re-suspending on key changes still works through it.\n React.useEffect(() => {\n if (!transient) return\n transient.setOptions({ ...transient.options, queryKey, enabled })\n }, [transient, queryKey, enabled])\n\n const observer = observerInScope ?? transient\n if (!observer) {\n throw new Error(\n '[@effector-tanstack-query/react] useSuspenseQuery: no QueryClient is set. ' +\n 'Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] }).',\n )\n }\n return observer\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { useUnit } from 'effector-react'\nimport { hydrate } from '@tanstack/query-core'\nimport type {\n DehydratedState,\n FetchStatus,\n HydrateOptions,\n MutateOptions,\n QueryStatus,\n} from '@tanstack/query-core'\nimport { $queryClient } from '@effector-tanstack-query/core'\nimport type {\n InfiniteQueryResult,\n MutationResult,\n MutationStatus,\n QueryResult,\n} from '@effector-tanstack-query/core'\n\nexport interface HydrationBoundaryProps {\n /**\n * Snapshot produced by `dehydrate(queryClient)` on the server. Re-applied\n * to the scope's `QueryClient` cache so observers mounted under this tree\n * read prefetched data instead of triggering fresh network requests.\n */\n state?: DehydratedState\n /** Forwarded to `hydrate(...)` — see `@tanstack/query-core` docs. */\n options?: HydrateOptions\n children?: React.ReactNode\n}\n\n/**\n * Merges a server-prefetched `DehydratedState` into the scope's\n * `QueryClient` cache.\n *\n * Mirrors `<HydrationBoundary>` from `@tanstack/react-query`: hydration\n * runs in `useMemo` so the merge happens during the render phase (children\n * see a populated cache on their first render, no flash). The hook\n * resolves the QueryClient via `useUnit($queryClient)` instead of\n * `useQueryClient()` — meaning each fork scope can have its own client\n * without an additional `<QueryClientProvider>` in the tree.\n *\n * `hydrate` is idempotent: re-rendering with the same `state` reference\n * is a no-op. Pass new `state` references on navigation to merge fresh\n * snapshots.\n *\n * Note: this only handles the QueryClient cache layer. Effector store\n * snapshots (e.g. `serialize(scope)`) flow through your existing\n * `<Provider>` / `<EffectorNext values>` layer — orthogonal concerns.\n */\nexport function HydrationBoundary({\n state,\n options,\n children,\n}: HydrationBoundaryProps): React.ReactElement {\n const queryClient = useUnit($queryClient)\n React.useMemo(() => {\n if (queryClient && state) hydrate(queryClient, state, options)\n }, [queryClient, state, options])\n return React.createElement(React.Fragment, null, children)\n}\n\nexport interface UseQueryResult<TData, TError = Error> {\n data: TData | undefined\n error: TError | null\n status: QueryStatus\n isPending: boolean\n isFetching: boolean\n isSuccess: boolean\n isError: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n refresh: () => void\n}\n\n/**\n * Subscribes a React component to a query, automatically calling\n * `mounted()` on mount and `unmounted()` on cleanup.\n */\nexport function useQuery<TData, TError = Error>(\n query: QueryResult<TData, TError>,\n): UseQueryResult<TData, TError> {\n const state = useUnit({\n data: query.$data,\n error: query.$error,\n status: query.$status,\n isPending: query.$isPending,\n isFetching: query.$isFetching,\n isSuccess: query.$isSuccess,\n isError: query.$isError,\n isPlaceholderData: query.$isPlaceholderData,\n fetchStatus: query.$fetchStatus,\n })\n\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n return { ...state, refresh }\n}\n\nexport interface UseMutationResult<TData, TError, TVariables> {\n data: TData | undefined\n error: TError | null\n status: MutationStatus\n variables: TVariables | undefined\n isPaused: boolean\n isPending: boolean\n isSuccess: boolean\n isError: boolean\n isIdle: boolean\n mutate: (variables: TVariables) => void\n /**\n * Trigger the mutation with per-call callbacks layered on top of the\n * observer-level ones (`onSuccess` / `onError` / `onSettled` in\n * `createMutation` options). Use this for component-local reactions\n * that don't fit module-level `sample` wiring — navigation after success,\n * one-shot toasts, etc.\n */\n mutateWith: (args: {\n variables: TVariables\n onSuccess?: MutateOptions<TData, TError, TVariables>['onSuccess']\n onError?: MutateOptions<TData, TError, TVariables>['onError']\n onSettled?: MutateOptions<TData, TError, TVariables>['onSettled']\n }) => void\n reset: () => void\n}\n\n/**\n * Subscribes a React component to a mutation, automatically calling\n * `start()` on mount and `unmounted()` on cleanup so the queryClient can\n * garbage-collect the mutation entry once no observers remain.\n */\nexport function useMutation<TData = unknown, TError = Error, TVariables = void>(\n mutation: MutationResult<TData, TError, TVariables>,\n): UseMutationResult<TData, TError, TVariables> {\n const state = useUnit({\n data: mutation.$data,\n error: mutation.$error,\n status: mutation.$status,\n variables: mutation.$variables,\n isPaused: mutation.$isPaused,\n isPending: mutation.$isPending,\n isSuccess: mutation.$isSuccess,\n isError: mutation.$isError,\n isIdle: mutation.$isIdle,\n })\n\n const start = useUnit(mutation.start)\n const unmount = useUnit(mutation.unmounted)\n const mutate = useUnit(mutation.mutate)\n const mutateWith = useUnit(mutation.mutateWith)\n const reset = useUnit(mutation.reset)\n\n React.useEffect(() => {\n start()\n return () => unmount()\n }, [start, unmount])\n\n return { ...state, mutate, mutateWith, reset }\n}\n\nexport interface UseInfiniteQueryResult<TData, TError> {\n data: TData | undefined\n error: TError | null\n status: QueryStatus\n isPending: boolean\n isFetching: boolean\n isSuccess: boolean\n isError: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n hasNextPage: boolean\n hasPreviousPage: boolean\n isFetchingNextPage: boolean\n isFetchingPreviousPage: boolean\n isFetchNextPageError: boolean\n isFetchPreviousPageError: boolean\n refresh: () => void\n fetchNextPage: () => void\n fetchPreviousPage: () => void\n}\n\n/**\n * Subscribes a React component to an infinite query, with auto mount/unmount\n * lifecycle and bound `fetchNextPage` / `fetchPreviousPage` callbacks.\n */\nexport function useInfiniteQuery<TData, TError = Error, TPageParam = unknown>(\n query: InfiniteQueryResult<TData, TError, TPageParam>,\n): UseInfiniteQueryResult<TData, TError> {\n const state = useUnit({\n data: query.$data,\n error: query.$error,\n status: query.$status,\n isPending: query.$isPending,\n isFetching: query.$isFetching,\n isSuccess: query.$isSuccess,\n isError: query.$isError,\n isPlaceholderData: query.$isPlaceholderData,\n fetchStatus: query.$fetchStatus,\n hasNextPage: query.$hasNextPage,\n hasPreviousPage: query.$hasPreviousPage,\n isFetchingNextPage: query.$isFetchingNextPage,\n isFetchingPreviousPage: query.$isFetchingPreviousPage,\n isFetchNextPageError: query.$isFetchNextPageError,\n isFetchPreviousPageError: query.$isFetchPreviousPageError,\n })\n\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n const fetchNextPage = useUnit(query.fetchNextPage)\n const fetchPreviousPage = useUnit(query.fetchPreviousPage)\n\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n return { ...state, refresh, fetchNextPage, fetchPreviousPage }\n}\n\n// Suspense data path: read from a per-scope observer.\n//\n// The scope mount chain runs in useEffect, which is skipped while a component\n// is suspended — so on the very first render the scope's `$observer` may be\n// null. To get synchronous access to the observer's promise during suspense,\n// we construct a transient observer via the factory's hidden\n// `__createObserver(qc, { queryKey, enabled })` helper. The transient observer\n// reads from / writes to the same queryClient cache as the eventual scope\n// observer (which is created when mountFx runs after useEffect commits).\n//\n// The mount/unmount effect is still wired up so that other consumers reading\n// the same query through `useUnit` / `useQuery` see updates in scope state.\n\nfunction useObserverRerender(\n observer: { subscribe: (cb: () => void) => () => void } | null,\n): void {\n const [, forceRender] = React.useReducer((x: number) => x + 1, 0)\n React.useEffect(() => {\n if (!observer) return\n return observer.subscribe(forceRender)\n }, [observer])\n}\n\ninterface SuspenseFactory<TObserver> {\n __createObserver(\n qc: import('@tanstack/query-core').QueryClient,\n init: { queryKey: unknown; enabled: boolean },\n ): TObserver\n __resolvedKey: import('effector').Store<unknown>\n __enabled: import('effector').Store<boolean>\n}\n\nexport interface UseSuspenseQueryResult<TData, TError = Error> {\n /** Resolved query data — non-nullable inside the rendered subtree (Suspense\n * absorbed the pending state). */\n data: TData\n /** Always `null` past the Suspense gate; errors are thrown to the nearest\n * `<ErrorBoundary>`. Typed as `TError | null` for consistency with\n * `useQuery` so the same destructure works in both. */\n error: TError | null\n status: 'success'\n isPending: false\n isSuccess: true\n isError: false\n /** `true` while a background refetch is running. Use for refresh spinners. */\n isFetching: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n refresh: () => void\n}\n\n/**\n * Reads a query for use inside a `<Suspense>` boundary. While the query is\n * pending, throws an inflight promise (queryClient-deduplicated). On error,\n * throws the error — catch with `<ErrorBoundary>`. Returns the same shape as\n * `useQuery`, but with `data` narrowed to non-nullable `TData` since the\n * pending state is impossible past the Suspense gate.\n */\nexport function useSuspenseQuery<TData, TError = Error>(\n query: QueryResult<TData, TError>,\n): UseSuspenseQueryResult<TData, TError> {\n // Auto-mount lifecycle so concurrent consumers (useUnit / useQuery) reading\n // the same query through the effector scope stay in sync.\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n const observer = useSuspenseObserver(query)\n useObserverRerender(observer)\n\n // Store snapshot — always read so hook order is fixed across renders.\n // The observer path doesn't use it; the store-only path (server-RSC of\n // a scope rehydrated from `serialize`, where `$observer` and\n // `$queryClient` are both `null` because they're `serialize: 'ignore'`)\n // reads everything from here.\n const state = useUnit({\n data: query.$data,\n error: query.$error,\n status: query.$status,\n isFetching: query.$isFetching,\n isPlaceholderData: query.$isPlaceholderData,\n fetchStatus: query.$fetchStatus,\n })\n\n // Observer path: `getOptimisticResult` reads from `QueryCache`\n // synchronously and reflects fetches/refetches the moment the observer\n // notifies. Used whenever we have an in-scope observer\n // (post-`mounted()`) or a transient one built from `$queryClient`.\n if (observer) {\n const result = observer.getOptimisticResult(observer.options as any)\n\n if (result.status === 'error') throw result.error\n if (result.status === 'pending') {\n throw observer.fetchOptimistic(observer.options as any)\n }\n\n return {\n data: result.data as TData,\n error: result.error as TError | null,\n status: 'success',\n isPending: false,\n isSuccess: true,\n isError: false,\n isFetching: result.isFetching,\n isPlaceholderData: result.isPlaceholderData,\n fetchStatus: result.fetchStatus,\n refresh,\n }\n }\n\n // Store-only path: no observer materialisable. Trust `$status` —\n // populated by `prefetchQueries` before the scope was serialised. A\n // pending status here means there's no `QueryClient` and no prefetch\n // happened: can't deduplicate-throw a fetch promise, so surface the\n // misconfiguration as an error to `<ErrorBoundary>`.\n if (state.status === 'error') throw state.error\n if (state.status === 'pending') {\n throw new Error(\n '[@effector-tanstack-query/react] useSuspenseQuery: no QueryClient is set. ' +\n 'Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] }).',\n )\n }\n\n return {\n data: state.data as TData,\n error: state.error as TError | null,\n status: 'success',\n isPending: false,\n isSuccess: true,\n isError: false,\n isFetching: state.isFetching,\n isPlaceholderData: state.isPlaceholderData,\n fetchStatus: state.fetchStatus,\n refresh,\n }\n}\n\nexport interface UseSuspenseInfiniteQueryResult<TData, TError = Error> {\n data: TData\n error: TError | null\n status: 'success'\n isPending: false\n isSuccess: true\n isError: false\n isFetching: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n hasNextPage: boolean\n hasPreviousPage: boolean\n isFetchingNextPage: boolean\n isFetchingPreviousPage: boolean\n isFetchNextPageError: boolean\n isFetchPreviousPageError: boolean\n refresh: () => void\n fetchNextPage: () => void\n fetchPreviousPage: () => void\n}\n\n/**\n * Suspense variant of {@link useInfiniteQuery}. Same shape as `useInfiniteQuery`,\n * with `data` narrowed to non-nullable.\n */\nexport function useSuspenseInfiniteQuery<\n TData,\n TError = Error,\n TPageParam = unknown,\n>(\n query: InfiniteQueryResult<TData, TError, TPageParam>,\n): UseSuspenseInfiniteQueryResult<TData, TError> {\n const mount = useUnit(query.mounted)\n const unmount = useUnit(query.unmounted)\n const refresh = useUnit(query.refresh)\n const fetchNextPage = useUnit(query.fetchNextPage)\n const fetchPreviousPage = useUnit(query.fetchPreviousPage)\n React.useEffect(() => {\n mount()\n return () => unmount()\n }, [mount, unmount])\n\n const observer = useSuspenseObserver(query)\n useObserverRerender(observer)\n\n // See `useSuspenseQuery` for the observer-vs-stores dual path rationale.\n // Infinite queries carry pagination fields in separate effector stores\n // (`$hasNextPage`, `$isFetchingNextPage`, …), so the store snapshot has\n // to read those too.\n const state = useUnit({\n data: query.$data,\n error: query.$error,\n status: query.$status,\n isFetching: query.$isFetching,\n isPlaceholderData: query.$isPlaceholderData,\n fetchStatus: query.$fetchStatus,\n hasNextPage: query.$hasNextPage,\n hasPreviousPage: query.$hasPreviousPage,\n isFetchingNextPage: query.$isFetchingNextPage,\n isFetchingPreviousPage: query.$isFetchingPreviousPage,\n isFetchNextPageError: query.$isFetchNextPageError,\n isFetchPreviousPageError: query.$isFetchPreviousPageError,\n })\n\n if (observer) {\n const obs = observer\n const result = obs.getOptimisticResult(obs.options as any)\n\n if (result.status === 'error') throw result.error\n if (result.status === 'pending') {\n throw (\n obs as unknown as {\n fetchOptimistic: (options: typeof obs.options) => Promise<unknown>\n }\n ).fetchOptimistic(obs.options)\n }\n\n const r = result as typeof result & {\n hasNextPage: boolean\n hasPreviousPage: boolean\n isFetchingNextPage: boolean\n isFetchingPreviousPage: boolean\n isFetchNextPageError: boolean\n isFetchPreviousPageError: boolean\n }\n\n return {\n data: r.data as TData,\n error: r.error as TError | null,\n status: 'success',\n isPending: false,\n isSuccess: true,\n isError: false,\n isFetching: r.isFetching,\n isPlaceholderData: r.isPlaceholderData,\n fetchStatus: r.fetchStatus,\n hasNextPage: r.hasNextPage,\n hasPreviousPage: r.hasPreviousPage,\n isFetchingNextPage: r.isFetchingNextPage,\n isFetchingPreviousPage: r.isFetchingPreviousPage,\n isFetchNextPageError: r.isFetchNextPageError,\n isFetchPreviousPageError: r.isFetchPreviousPageError,\n refresh,\n fetchNextPage,\n fetchPreviousPage,\n }\n }\n\n // Store-only path (server-RSC of a hydrated scope).\n if (state.status === 'error') throw state.error\n if (state.status === 'pending') {\n throw new Error(\n '[@effector-tanstack-query/react] useSuspenseInfiniteQuery: no QueryClient is set. ' +\n 'Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] }).',\n )\n }\n\n return {\n data: state.data as TData,\n error: state.error as TError | null,\n status: 'success',\n isPending: false,\n isSuccess: true,\n isError: false,\n isFetching: state.isFetching,\n isPlaceholderData: state.isPlaceholderData,\n fetchStatus: state.fetchStatus,\n hasNextPage: state.hasNextPage,\n hasPreviousPage: state.hasPreviousPage,\n isFetchingNextPage: state.isFetchingNextPage,\n isFetchingPreviousPage: state.isFetchingPreviousPage,\n isFetchNextPageError: state.isFetchNextPageError,\n isFetchPreviousPageError: state.isFetchPreviousPageError,\n refresh,\n fetchNextPage,\n fetchPreviousPage,\n }\n}\n\n/**\n * Resolves a per-scope observer for suspense usage. Prefers the scope's\n * `$observer` (set by mountFx); falls back to a transient observer\n * constructed via `__createObserver` so that the very first render — before\n * useEffect has fired — has a working observer. Both flavors read/write the\n * same queryClient cache, so the transient observer is a thin wrapper.\n */\nfunction useSuspenseObserver<\n TQuery extends {\n $observer: import('effector').Store<TObserver | null>\n $queryClient: import('effector').Store<\n import('@tanstack/query-core').QueryClient | null\n >\n },\n TObserver extends {\n options: { queryKey: unknown }\n setOptions(options: any): void\n subscribe(cb: () => void): () => void\n getOptimisticResult(options: any): {\n status: 'pending' | 'success' | 'error'\n data: unknown\n error: unknown\n isFetching: boolean\n isPlaceholderData: boolean\n fetchStatus: FetchStatus\n // Infinite-query result fields — present at runtime when the underlying\n // observer is an InfiniteQueryObserver; the suspense hooks narrow as\n // needed. Typed as `any` here to keep the constraint loose.\n hasNextPage?: any\n hasPreviousPage?: any\n isFetchingNextPage?: any\n isFetchingPreviousPage?: any\n isFetchNextPageError?: any\n isFetchPreviousPageError?: any\n }\n fetchOptimistic(options: any): Promise<unknown>\n },\n>(query: TQuery): TObserver | null {\n const factory = query as unknown as TQuery & SuspenseFactory<TObserver>\n const observerInScope = useUnit(query.$observer) as TObserver | null\n const qc = useUnit(query.$queryClient)\n const queryKey = useUnit(factory.__resolvedKey)\n const enabled = useUnit(factory.__enabled)\n\n // Memoize a transient observer keyed by qc, so it survives across renders\n // while the scope observer is null. Once observerInScope appears, we\n // switch — the transient one is unsubscribed and abandoned (it never\n // subscribed to queryCache, so there is nothing to leak).\n const transient = React.useMemo(() => {\n if (observerInScope || !qc) return null\n return factory.__createObserver(qc, { queryKey, enabled })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [observerInScope, qc, factory])\n\n // Keep the transient observer's options in sync with reactive key/enabled,\n // so re-suspending on key changes still works through it.\n React.useEffect(() => {\n if (!transient) return\n transient.setOptions({ ...transient.options, queryKey, enabled })\n }, [transient, queryKey, enabled])\n\n // Null is a legitimate return: server-RSC render of a scope built from\n // `serialize(scope)` has neither `$observer` nor `$queryClient` (both are\n // `serialize: 'ignore'` — instances can't ride through the RSC boundary).\n // Callers branch on `$status === 'success'` from the serialized stores;\n // they only error out when a pending state is unreachable without an\n // observer to throw `fetchOptimistic` on.\n return observerInScope ?? transient\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effector-tanstack-query/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "React hooks for @effector-tanstack-query/core — useQuery, useMutation, useInfiniteQuery, useSuspenseQuery, useSuspenseInfiniteQuery",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ilya Agarkov <ilya.al.ag@gmail.com>",
|
|
@@ -36,6 +36,16 @@
|
|
|
36
36
|
"default": "./dist/index.cjs"
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
|
+
"./compat": {
|
|
40
|
+
"import": {
|
|
41
|
+
"types": "./dist/compat.d.ts",
|
|
42
|
+
"default": "./dist/compat.js"
|
|
43
|
+
},
|
|
44
|
+
"require": {
|
|
45
|
+
"types": "./dist/compat.d.cts",
|
|
46
|
+
"default": "./dist/compat.cjs"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
39
49
|
"./package.json": "./package.json"
|
|
40
50
|
},
|
|
41
51
|
"files": [
|
|
@@ -45,14 +55,23 @@
|
|
|
45
55
|
],
|
|
46
56
|
"sideEffects": false,
|
|
47
57
|
"dependencies": {
|
|
48
|
-
"@effector-tanstack-query/core": "0.
|
|
58
|
+
"@effector-tanstack-query/core": "0.3.0"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@tanstack/react-query": "^5.0.0"
|
|
49
62
|
},
|
|
50
63
|
"peerDependencies": {
|
|
51
64
|
"@tanstack/query-core": "^5.0.0",
|
|
65
|
+
"@tanstack/react-query": "^5.0.0",
|
|
52
66
|
"effector": ">=23.0.0",
|
|
53
67
|
"effector-react": ">=23.0.0",
|
|
54
68
|
"react": "^18.0.0 || ^19.0.0"
|
|
55
69
|
},
|
|
70
|
+
"peerDependenciesMeta": {
|
|
71
|
+
"@tanstack/react-query": {
|
|
72
|
+
"optional": true
|
|
73
|
+
}
|
|
74
|
+
},
|
|
56
75
|
"scripts": {
|
|
57
76
|
"build": "tsup && node ./scripts/inject-use-client.mjs",
|
|
58
77
|
"build:watch": "tsup --watch",
|
package/src/compat.tsx
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { useUnit } from 'effector-react'
|
|
5
|
+
import {
|
|
6
|
+
isServer,
|
|
7
|
+
QueryClient,
|
|
8
|
+
QueryClientProvider,
|
|
9
|
+
} from '@tanstack/react-query'
|
|
10
|
+
import { $queryClient } from '@effector-tanstack-query/core'
|
|
11
|
+
|
|
12
|
+
type DefaultOptions = NonNullable<
|
|
13
|
+
ConstructorParameters<typeof QueryClient>[0]
|
|
14
|
+
>['defaultOptions']
|
|
15
|
+
|
|
16
|
+
export interface QueryClientCompatProviderProps {
|
|
17
|
+
children: React.ReactNode
|
|
18
|
+
/**
|
|
19
|
+
* `defaultOptions` for the per-render server fallback `QueryClient`
|
|
20
|
+
* (see below). Has no effect on the client — the browser singleton
|
|
21
|
+
* built in your own top-level `<EffectorNext>` setup is what `useQuery`
|
|
22
|
+
* from `@tanstack/react-query` resolves to there. Keep these aligned
|
|
23
|
+
* with your `createQuery({ staleTime, retry, ... })` definitions so
|
|
24
|
+
* server-rendered HTML matches what the client will produce after
|
|
25
|
+
* hydration.
|
|
26
|
+
*/
|
|
27
|
+
defaultOptions?: DefaultOptions
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* SSR-safe `QueryClientProvider` bridge for users mixing
|
|
32
|
+
* `@tanstack/react-query` and `@effector-tanstack-query` — the typical
|
|
33
|
+
* "we're migrating page by page" case.
|
|
34
|
+
*
|
|
35
|
+
* Why a dedicated component:
|
|
36
|
+
*
|
|
37
|
+
* - **Client**: `useUnit($queryClient)` returns the singleton browser
|
|
38
|
+
* `QueryClient` already mounted by your top-level provider (whichever
|
|
39
|
+
* module calls `setQueryClient(qc)` + `allSettled($queryClient, ...)`
|
|
40
|
+
* for `getClientScope()`). Handing that same instance to
|
|
41
|
+
* `<QueryClientProvider>` means **both APIs share a cache**: prefetch
|
|
42
|
+
* with one, read with the other; `setQueryData` /
|
|
43
|
+
* `invalidateQueries` from one re-renders the other.
|
|
44
|
+
*
|
|
45
|
+
* - **Server (RSC)**: `$queryClient` is `serialize: 'ignore'` (its
|
|
46
|
+
* value is a class instance — not JSON-safe — and per-request
|
|
47
|
+
* QueryClients must NEVER be a module-level singleton on the
|
|
48
|
+
* server). So inside the RSC rendering scope produced by
|
|
49
|
+
* `<EffectorNext values={serialize(scope)}>`, `useUnit($queryClient)`
|
|
50
|
+
* resolves to `null`. We fall back to a throwaway per-render
|
|
51
|
+
* `QueryClient` solely so vanilla `useQuery` has a provider during
|
|
52
|
+
* the server pass. Pair this component with `<HydrationBoundary>`
|
|
53
|
+
* from `@tanstack/react-query` — the boundary will hydrate this
|
|
54
|
+
* fallback client with the prefetched cache, so server-rendered
|
|
55
|
+
* HTML shows data instead of a loading flash. On client hydration
|
|
56
|
+
* the provider re-renders with the singleton browser client and
|
|
57
|
+
* react-query re-subscribes its observers.
|
|
58
|
+
*
|
|
59
|
+
* `useState` makes the fallback per-component-instance (one per render
|
|
60
|
+
* tree), not a module-level singleton — concurrent SSR requests don't
|
|
61
|
+
* share it.
|
|
62
|
+
*/
|
|
63
|
+
export function QueryClientCompatProvider({
|
|
64
|
+
children,
|
|
65
|
+
defaultOptions,
|
|
66
|
+
}: QueryClientCompatProviderProps): React.ReactElement {
|
|
67
|
+
const fromEffector = useUnit($queryClient)
|
|
68
|
+
const [serverFallback] = React.useState<QueryClient | null>(() =>
|
|
69
|
+
isServer ? new QueryClient({ defaultOptions }) : null,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
const qc = fromEffector ?? serverFallback
|
|
73
|
+
if (!qc) {
|
|
74
|
+
// Defensive: on the client `fromEffector` is set synchronously by
|
|
75
|
+
// your top-level provider before any render, so this branch is
|
|
76
|
+
// unreachable in practice. If it fires, rendering children without
|
|
77
|
+
// a provider would crash any descendant `useQuery` from
|
|
78
|
+
// `@tanstack/react-query`.
|
|
79
|
+
return <>{children}</>
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return <QueryClientProvider client={qc}>{children}</QueryClientProvider>
|
|
83
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -239,11 +239,14 @@ export function useInfiniteQuery<TData, TError = Error, TPageParam = unknown>(
|
|
|
239
239
|
// The mount/unmount effect is still wired up so that other consumers reading
|
|
240
240
|
// the same query through `useUnit` / `useQuery` see updates in scope state.
|
|
241
241
|
|
|
242
|
-
function useObserverRerender(
|
|
243
|
-
subscribe: (cb: () => void) => () => void
|
|
244
|
-
|
|
242
|
+
function useObserverRerender(
|
|
243
|
+
observer: { subscribe: (cb: () => void) => () => void } | null,
|
|
244
|
+
): void {
|
|
245
245
|
const [, forceRender] = React.useReducer((x: number) => x + 1, 0)
|
|
246
|
-
React.useEffect(() =>
|
|
246
|
+
React.useEffect(() => {
|
|
247
|
+
if (!observer) return
|
|
248
|
+
return observer.subscribe(forceRender)
|
|
249
|
+
}, [observer])
|
|
247
250
|
}
|
|
248
251
|
|
|
249
252
|
interface SuspenseFactory<TObserver> {
|
|
@@ -295,30 +298,71 @@ export function useSuspenseQuery<TData, TError = Error>(
|
|
|
295
298
|
}, [mount, unmount])
|
|
296
299
|
|
|
297
300
|
const observer = useSuspenseObserver(query)
|
|
298
|
-
|
|
299
301
|
useObserverRerender(observer)
|
|
300
302
|
|
|
301
|
-
|
|
303
|
+
// Store snapshot — always read so hook order is fixed across renders.
|
|
304
|
+
// The observer path doesn't use it; the store-only path (server-RSC of
|
|
305
|
+
// a scope rehydrated from `serialize`, where `$observer` and
|
|
306
|
+
// `$queryClient` are both `null` because they're `serialize: 'ignore'`)
|
|
307
|
+
// reads everything from here.
|
|
308
|
+
const state = useUnit({
|
|
309
|
+
data: query.$data,
|
|
310
|
+
error: query.$error,
|
|
311
|
+
status: query.$status,
|
|
312
|
+
isFetching: query.$isFetching,
|
|
313
|
+
isPlaceholderData: query.$isPlaceholderData,
|
|
314
|
+
fetchStatus: query.$fetchStatus,
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
// Observer path: `getOptimisticResult` reads from `QueryCache`
|
|
318
|
+
// synchronously and reflects fetches/refetches the moment the observer
|
|
319
|
+
// notifies. Used whenever we have an in-scope observer
|
|
320
|
+
// (post-`mounted()`) or a transient one built from `$queryClient`.
|
|
321
|
+
if (observer) {
|
|
322
|
+
const result = observer.getOptimisticResult(observer.options as any)
|
|
323
|
+
|
|
324
|
+
if (result.status === 'error') throw result.error
|
|
325
|
+
if (result.status === 'pending') {
|
|
326
|
+
throw observer.fetchOptimistic(observer.options as any)
|
|
327
|
+
}
|
|
302
328
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
329
|
+
return {
|
|
330
|
+
data: result.data as TData,
|
|
331
|
+
error: result.error as TError | null,
|
|
332
|
+
status: 'success',
|
|
333
|
+
isPending: false,
|
|
334
|
+
isSuccess: true,
|
|
335
|
+
isError: false,
|
|
336
|
+
isFetching: result.isFetching,
|
|
337
|
+
isPlaceholderData: result.isPlaceholderData,
|
|
338
|
+
fetchStatus: result.fetchStatus,
|
|
339
|
+
refresh,
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Store-only path: no observer materialisable. Trust `$status` —
|
|
344
|
+
// populated by `prefetchQueries` before the scope was serialised. A
|
|
345
|
+
// pending status here means there's no `QueryClient` and no prefetch
|
|
346
|
+
// happened: can't deduplicate-throw a fetch promise, so surface the
|
|
347
|
+
// misconfiguration as an error to `<ErrorBoundary>`.
|
|
348
|
+
if (state.status === 'error') throw state.error
|
|
349
|
+
if (state.status === 'pending') {
|
|
350
|
+
throw new Error(
|
|
351
|
+
'[@effector-tanstack-query/react] useSuspenseQuery: no QueryClient is set. ' +
|
|
352
|
+
'Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] }).',
|
|
353
|
+
)
|
|
306
354
|
}
|
|
307
355
|
|
|
308
|
-
// Read all secondary fields from the observer result, not the effector
|
|
309
|
-
// stores: stores are only populated after mountFx fires from useEffect,
|
|
310
|
-
// which on the very first successful render hasn't run yet. The observer
|
|
311
|
-
// result is always live and consistent.
|
|
312
356
|
return {
|
|
313
|
-
data:
|
|
314
|
-
error:
|
|
357
|
+
data: state.data as TData,
|
|
358
|
+
error: state.error as TError | null,
|
|
315
359
|
status: 'success',
|
|
316
360
|
isPending: false,
|
|
317
361
|
isSuccess: true,
|
|
318
362
|
isError: false,
|
|
319
|
-
isFetching:
|
|
320
|
-
isPlaceholderData:
|
|
321
|
-
fetchStatus:
|
|
363
|
+
isFetching: state.isFetching,
|
|
364
|
+
isPlaceholderData: state.isPlaceholderData,
|
|
365
|
+
fetchStatus: state.fetchStatus,
|
|
322
366
|
refresh,
|
|
323
367
|
}
|
|
324
368
|
}
|
|
@@ -366,47 +410,96 @@ export function useSuspenseInfiniteQuery<
|
|
|
366
410
|
}, [mount, unmount])
|
|
367
411
|
|
|
368
412
|
const observer = useSuspenseObserver(query)
|
|
369
|
-
|
|
370
413
|
useObserverRerender(observer)
|
|
371
414
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
415
|
+
// See `useSuspenseQuery` for the observer-vs-stores dual path rationale.
|
|
416
|
+
// Infinite queries carry pagination fields in separate effector stores
|
|
417
|
+
// (`$hasNextPage`, `$isFetchingNextPage`, …), so the store snapshot has
|
|
418
|
+
// to read those too.
|
|
419
|
+
const state = useUnit({
|
|
420
|
+
data: query.$data,
|
|
421
|
+
error: query.$error,
|
|
422
|
+
status: query.$status,
|
|
423
|
+
isFetching: query.$isFetching,
|
|
424
|
+
isPlaceholderData: query.$isPlaceholderData,
|
|
425
|
+
fetchStatus: query.$fetchStatus,
|
|
426
|
+
hasNextPage: query.$hasNextPage,
|
|
427
|
+
hasPreviousPage: query.$hasPreviousPage,
|
|
428
|
+
isFetchingNextPage: query.$isFetchingNextPage,
|
|
429
|
+
isFetchingPreviousPage: query.$isFetchingPreviousPage,
|
|
430
|
+
isFetchNextPageError: query.$isFetchNextPageError,
|
|
431
|
+
isFetchPreviousPageError: query.$isFetchPreviousPageError,
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
if (observer) {
|
|
435
|
+
const obs = observer
|
|
436
|
+
const result = obs.getOptimisticResult(obs.options as any)
|
|
437
|
+
|
|
438
|
+
if (result.status === 'error') throw result.error
|
|
439
|
+
if (result.status === 'pending') {
|
|
440
|
+
throw (
|
|
441
|
+
obs as unknown as {
|
|
442
|
+
fetchOptimistic: (options: typeof obs.options) => Promise<unknown>
|
|
443
|
+
}
|
|
444
|
+
).fetchOptimistic(obs.options)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const r = result as typeof result & {
|
|
448
|
+
hasNextPage: boolean
|
|
449
|
+
hasPreviousPage: boolean
|
|
450
|
+
isFetchingNextPage: boolean
|
|
451
|
+
isFetchingPreviousPage: boolean
|
|
452
|
+
isFetchNextPageError: boolean
|
|
453
|
+
isFetchPreviousPageError: boolean
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return {
|
|
457
|
+
data: r.data as TData,
|
|
458
|
+
error: r.error as TError | null,
|
|
459
|
+
status: 'success',
|
|
460
|
+
isPending: false,
|
|
461
|
+
isSuccess: true,
|
|
462
|
+
isError: false,
|
|
463
|
+
isFetching: r.isFetching,
|
|
464
|
+
isPlaceholderData: r.isPlaceholderData,
|
|
465
|
+
fetchStatus: r.fetchStatus,
|
|
466
|
+
hasNextPage: r.hasNextPage,
|
|
467
|
+
hasPreviousPage: r.hasPreviousPage,
|
|
468
|
+
isFetchingNextPage: r.isFetchingNextPage,
|
|
469
|
+
isFetchingPreviousPage: r.isFetchingPreviousPage,
|
|
470
|
+
isFetchNextPageError: r.isFetchNextPageError,
|
|
471
|
+
isFetchPreviousPageError: r.isFetchPreviousPageError,
|
|
472
|
+
refresh,
|
|
473
|
+
fetchNextPage,
|
|
474
|
+
fetchPreviousPage,
|
|
475
|
+
}
|
|
383
476
|
}
|
|
384
477
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
478
|
+
// Store-only path (server-RSC of a hydrated scope).
|
|
479
|
+
if (state.status === 'error') throw state.error
|
|
480
|
+
if (state.status === 'pending') {
|
|
481
|
+
throw new Error(
|
|
482
|
+
'[@effector-tanstack-query/react] useSuspenseInfiniteQuery: no QueryClient is set. ' +
|
|
483
|
+
'Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] }).',
|
|
484
|
+
)
|
|
392
485
|
}
|
|
393
486
|
|
|
394
487
|
return {
|
|
395
|
-
data:
|
|
396
|
-
error:
|
|
488
|
+
data: state.data as TData,
|
|
489
|
+
error: state.error as TError | null,
|
|
397
490
|
status: 'success',
|
|
398
491
|
isPending: false,
|
|
399
492
|
isSuccess: true,
|
|
400
493
|
isError: false,
|
|
401
|
-
isFetching:
|
|
402
|
-
isPlaceholderData:
|
|
403
|
-
fetchStatus:
|
|
404
|
-
hasNextPage:
|
|
405
|
-
hasPreviousPage:
|
|
406
|
-
isFetchingNextPage:
|
|
407
|
-
isFetchingPreviousPage:
|
|
408
|
-
isFetchNextPageError:
|
|
409
|
-
isFetchPreviousPageError:
|
|
494
|
+
isFetching: state.isFetching,
|
|
495
|
+
isPlaceholderData: state.isPlaceholderData,
|
|
496
|
+
fetchStatus: state.fetchStatus,
|
|
497
|
+
hasNextPage: state.hasNextPage,
|
|
498
|
+
hasPreviousPage: state.hasPreviousPage,
|
|
499
|
+
isFetchingNextPage: state.isFetchingNextPage,
|
|
500
|
+
isFetchingPreviousPage: state.isFetchingPreviousPage,
|
|
501
|
+
isFetchNextPageError: state.isFetchNextPageError,
|
|
502
|
+
isFetchPreviousPageError: state.isFetchPreviousPageError,
|
|
410
503
|
refresh,
|
|
411
504
|
fetchNextPage,
|
|
412
505
|
fetchPreviousPage,
|
|
@@ -450,7 +543,7 @@ function useSuspenseObserver<
|
|
|
450
543
|
}
|
|
451
544
|
fetchOptimistic(options: any): Promise<unknown>
|
|
452
545
|
},
|
|
453
|
-
>(query: TQuery): TObserver {
|
|
546
|
+
>(query: TQuery): TObserver | null {
|
|
454
547
|
const factory = query as unknown as TQuery & SuspenseFactory<TObserver>
|
|
455
548
|
const observerInScope = useUnit(query.$observer) as TObserver | null
|
|
456
549
|
const qc = useUnit(query.$queryClient)
|
|
@@ -474,12 +567,11 @@ function useSuspenseObserver<
|
|
|
474
567
|
transient.setOptions({ ...transient.options, queryKey, enabled })
|
|
475
568
|
}, [transient, queryKey, enabled])
|
|
476
569
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
return observer
|
|
570
|
+
// Null is a legitimate return: server-RSC render of a scope built from
|
|
571
|
+
// `serialize(scope)` has neither `$observer` nor `$queryClient` (both are
|
|
572
|
+
// `serialize: 'ignore'` — instances can't ride through the RSC boundary).
|
|
573
|
+
// Callers branch on `$status === 'success'` from the serialized stores;
|
|
574
|
+
// they only error out when a pending state is unreachable without an
|
|
575
|
+
// observer to throw `fetchOptimistic` on.
|
|
576
|
+
return observerInScope ?? transient
|
|
485
577
|
}
|