|
4 | 4 | */ |
5 | 5 |
|
6 | 6 | 'use client'; |
7 | | -import type { QueryKey, QueryObserver, QueryObserverResult } from '@tanstack/query-core'; |
| 7 | +import type { DefaultedQueryObserverOptions, QueryKey, QueryObserver, QueryObserverResult } from '@tanstack/query-core'; |
8 | 8 | import { noop, notifyManager } from '@tanstack/query-core'; |
9 | 9 | import * as React from 'react'; |
10 | 10 |
|
11 | 11 | import type { UseBaseQueryOptions } from './types'; |
12 | 12 | import { useClerkQueryClient } from './use-clerk-query-client'; |
13 | 13 |
|
| 14 | +export type DistributivePick<T, K extends PropertyKey> = T extends unknown ? Pick<T, Extract<K, keyof T>> : never; |
| 15 | + |
| 16 | +export type CommonQueryResult = 'data' | 'error' | 'isLoading' | 'isFetching' | 'status'; |
| 17 | + |
14 | 18 | /** |
15 | 19 | * |
16 | 20 | */ |
17 | 21 | export function useBaseQuery<TQueryFnData, TError, TData, TQueryData, TQueryKey extends QueryKey>( |
18 | 22 | options: UseBaseQueryOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>, |
19 | 23 | Observer: typeof QueryObserver, |
20 | | -): QueryObserverResult<TData, TError> { |
21 | | - const client = useClerkQueryClient(); |
22 | | - const defaultedOptions = client.defaultQueryOptions(options); |
| 24 | +): DistributivePick<QueryObserverResult<TData, TError>, CommonQueryResult> { |
| 25 | + const [client, isQueryClientLoaded] = useClerkQueryClient(); |
| 26 | + const defaultedOptions = isQueryClientLoaded |
| 27 | + ? client.defaultQueryOptions(options) |
| 28 | + : (options as DefaultedQueryObserverOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>); |
| 29 | + |
| 30 | + // Make sure results are optimistically set in fetching state before subscribing or updating options |
| 31 | + defaultedOptions._optimisticResults = 'optimistic'; |
23 | 32 |
|
24 | 33 | const observer = React.useMemo(() => { |
25 | 34 | return new Observer<TQueryFnData, TError, TData, TQueryData, TQueryKey>(client, defaultedOptions); |
@@ -50,6 +59,19 @@ export function useBaseQuery<TQueryFnData, TError, TData, TQueryData, TQueryKey |
50 | 59 | observer.setOptions(defaultedOptions); |
51 | 60 | }, [defaultedOptions, observer]); |
52 | 61 |
|
| 62 | + if (!isQueryClientLoaded) { |
| 63 | + // In this step we attempt to return a dummy result that matches RQ's pending state while on SSR or untill the query client is loaded on the client (after clerk-js loads). |
| 64 | + // When the query client is not loaded, we return the result as if the query was not enabled. |
| 65 | + // `isLoading` and `isFetching` need to be `false` because we can't know if the query will be enabled during SSR since most conditions really on client-only data that are available after clerk-js loads. |
| 66 | + return { |
| 67 | + data: undefined, |
| 68 | + error: null, |
| 69 | + isLoading: false, |
| 70 | + isFetching: false, |
| 71 | + status: 'pending', |
| 72 | + }; |
| 73 | + } |
| 74 | + |
53 | 75 | // Handle result property usage tracking |
54 | 76 | return !defaultedOptions.notifyOnChangeProps ? observer.trackResult(result) : result; |
55 | 77 | } |
0 commit comments