diff --git a/packages/solid-query/src/createBaseQuery.ts b/packages/solid-query/src/createBaseQuery.ts index 8b4876df15..541a7cf044 100644 --- a/packages/solid-query/src/createBaseQuery.ts +++ b/packages/solid-query/src/createBaseQuery.ts @@ -19,12 +19,10 @@ import type { CreateBaseQueryOptions } from './types' import type { Accessor, Signal } from 'solid-js' import type { QueryClient } from './QueryClient' import type { - InfiniteQueryObserverResult, Query, QueryKey, QueryObserver, QueryObserverResult, - QueryState, } from '@tanstack/query-core' function reconcileFn( @@ -62,10 +60,6 @@ function reconcileFn( return { ...result, data: newData } as typeof result } -type HydratableQueryState = QueryObserverResult & - QueryState & - InfiniteQueryObserverResult - /** * Solid's `onHydrated` functionality will silently "fail" (hydrate with an empty object) * if the resource data is not serializable. @@ -80,42 +74,29 @@ const hydratableObserverResult = < query: Query, result: QueryObserverResult, ) => { - // Including the extra properties is only relevant on the server - if (!isServer) return result as HydratableQueryState - - return { + const obj: any = { ...unwrap(result), + // During SSR, functions cannot be serialized, so we need to remove them + // This is safe because we will add these functions back when the query is hydrated + refetch: undefined, + } - // cast to refetch function should be safe, since we only remove it on the server, - // and refetch is not relevant on the server - refetch: undefined as unknown as HydratableQueryState< - TDataHydratable, - TError - >['refetch'], - - // cast to fetchNextPage function should be safe, since we only remove it on the server, - fetchNextPage: undefined as unknown as HydratableQueryState< - TDataHydratable, - TError - >['fetchNextPage'], - - // cast to fetchPreviousPage function should be safe, since we only remove it on the server, - fetchPreviousPage: undefined as unknown as HydratableQueryState< - TDataHydratable, - TError - >['fetchPreviousPage'], + // If the query is an infinite query, we need to remove additional properties + if ('fetchNextPage' in result) { + obj.fetchNextPage = undefined + obj.fetchPreviousPage = undefined + } - // hydrate() expects a QueryState object, which is similar but not - // quite the same as a QueryObserverResult object. Thus, for now, we're - // copying over the missing properties from state in order to support hydration - dataUpdateCount: query.state.dataUpdateCount, - fetchFailureCount: query.state.fetchFailureCount, - isInvalidated: query.state.isInvalidated, + // We will also attach the dehydrated state of the query to the result + // This will be removed on client after hydration + obj.hydrationData = { + state: query.state, + queryKey: query.queryKey, + queryHash: query.queryHash, + ...(query.meta && { meta: query.meta }), + } - // Unsetting these properties on the server since they might not be serializable - fetchFailureReason: null, - fetchMeta: null, - } as HydratableQueryState + return obj } // Base Query Function that is used to create the query. @@ -132,9 +113,7 @@ export function createBaseQuery< Observer: typeof QueryObserver, queryClient?: Accessor, ) { - type ResourceData = - | HydratableQueryState - | QueryObserverResult + type ResourceData = QueryObserverResult const client = createMemo(() => useQueryClient(queryClient?.())) const isRestoring = useIsRestoring() @@ -221,12 +200,18 @@ export function createBaseQuery< function createDeepSignal(): Signal { return [ () => state, - (v: T) => { + (v: any) => { const unwrapped = unwrap(state) if (typeof v === 'function') { v = v(unwrapped) } - setStateWithReconciliation(v as any) + // Hydration data exists on first load after SSR, + // and should be removed from the observer result + if (v.hydrationData) { + const { hydrationData, ...rest } = v + v = rest + } + setStateWithReconciliation(v) }, ] as Signal } @@ -269,9 +254,8 @@ export function createBaseQuery< return reject(observerResult.error) } if (!observerResult.isLoading) { - const query = obs.getCurrentQuery() resolver = null - return resolve(hydratableObserverResult(query, observerResult)) + return resolve(observerResult) } setStateWithReconciliation(observerResult) @@ -293,15 +277,10 @@ export function createBaseQuery< * Note that this is only invoked on the client, for queries that were originally run on the server. */ onHydrated(_k, info) { - if (info.value) { + if (info.value && 'hydrationData' in info.value) { hydrate(client(), { - queries: [ - { - queryKey: initialOptions.queryKey, - queryHash: initialOptions.queryHash, - state: info.value, - }, - ], + // @ts-expect-error - hydrationData is not correctly typed internally + queries: [{ ...info.value.hydrationData }], }) }