@effector-tanstack-query/react 0.3.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/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(() => observer.subscribe(forceRender), [observer]);
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 result = observer.getOptimisticResult(observer.options);
128
- if (result.status === "error") throw result.error;
129
- if (result.status === "pending") {
130
- throw observer.fetchOptimistic(observer.options);
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: result.data,
134
- error: result.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: result.isFetching,
140
- isPlaceholderData: result.isPlaceholderData,
141
- fetchStatus: result.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 result = observer.getOptimisticResult(observer.options);
158
- if (result.status === "error") throw result.error;
159
- if (result.status === "pending") {
160
- throw observer.fetchOptimistic(observer.options);
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: r.data,
165
- error: r.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: r.isFetching,
171
- isPlaceholderData: r.isPlaceholderData,
172
- fetchStatus: r.fetchStatus,
173
- hasNextPage: r.hasNextPage,
174
- hasPreviousPage: r.hasPreviousPage,
175
- isFetchingNextPage: r.isFetchingNextPage,
176
- isFetchingPreviousPage: r.isFetchingPreviousPage,
177
- isFetchNextPageError: r.isFetchNextPageError,
178
- isFetchPreviousPageError: r.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
- const observer = observerInScope ?? transient;
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;
@@ -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(() => observer.subscribe(forceRender), [observer]);
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 result = observer.getOptimisticResult(observer.options);
106
- if (result.status === "error") throw result.error;
107
- if (result.status === "pending") {
108
- throw observer.fetchOptimistic(observer.options);
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: result.data,
112
- error: result.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: result.isFetching,
118
- isPlaceholderData: result.isPlaceholderData,
119
- fetchStatus: result.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 result = observer.getOptimisticResult(observer.options);
136
- if (result.status === "error") throw result.error;
137
- if (result.status === "pending") {
138
- throw observer.fetchOptimistic(observer.options);
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: r.data,
143
- error: r.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: r.isFetching,
149
- isPlaceholderData: r.isPlaceholderData,
150
- fetchStatus: r.fetchStatus,
151
- hasNextPage: r.hasNextPage,
152
- hasPreviousPage: r.hasPreviousPage,
153
- isFetchingNextPage: r.isFetchingNextPage,
154
- isFetchingPreviousPage: r.isFetchingPreviousPage,
155
- isFetchNextPageError: r.isFetchNextPageError,
156
- isFetchPreviousPageError: r.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
- const observer = observerInScope ?? transient;
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.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>",
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(observer: {
243
- subscribe: (cb: () => void) => () => void
244
- }): void {
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(() => observer.subscribe(forceRender), [observer])
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
- const result = observer.getOptimisticResult(observer.options as any)
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
- if (result.status === 'error') throw result.error
304
- if (result.status === 'pending') {
305
- throw observer.fetchOptimistic(observer.options as any)
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: result.data as TData,
314
- error: result.error as TError | null,
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: result.isFetching,
320
- isPlaceholderData: result.isPlaceholderData,
321
- fetchStatus: result.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
- const result = observer.getOptimisticResult(observer.options as any)
373
-
374
- if (result.status === 'error') throw result.error
375
- if (result.status === 'pending') {
376
- throw (
377
- observer as unknown as {
378
- fetchOptimistic: (
379
- options: typeof observer.options,
380
- ) => Promise<unknown>
381
- }
382
- ).fetchOptimistic(observer.options)
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
- const r = result as typeof result & {
386
- hasNextPage: boolean
387
- hasPreviousPage: boolean
388
- isFetchingNextPage: boolean
389
- isFetchingPreviousPage: boolean
390
- isFetchNextPageError: boolean
391
- isFetchPreviousPageError: boolean
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: r.data as TData,
396
- error: r.error as TError | null,
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: r.isFetching,
402
- isPlaceholderData: r.isPlaceholderData,
403
- fetchStatus: r.fetchStatus,
404
- hasNextPage: r.hasNextPage,
405
- hasPreviousPage: r.hasPreviousPage,
406
- isFetchingNextPage: r.isFetchingNextPage,
407
- isFetchingPreviousPage: r.isFetchingPreviousPage,
408
- isFetchNextPageError: r.isFetchNextPageError,
409
- isFetchPreviousPageError: r.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
- const observer = observerInScope ?? transient
478
- if (!observer) {
479
- throw new Error(
480
- '[@effector-tanstack-query/react] useSuspenseQuery: no QueryClient is set. ' +
481
- 'Call setQueryClient(qc) or pass it to fork({ values: [[$queryClient, qc]] }).',
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
  }