Skip to content

Commit

Permalink
fix(solid-query): Persist client implementation (#6993)
Browse files Browse the repository at this point in the history

Co-authored-by: Brendonovich  <brendonovich@outlook.com>
  • Loading branch information
ardeora and Brendonovich committed Feb 28, 2024
1 parent b7b5096 commit c69a18a
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 69 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { persistQueryClient } from '@tanstack/query-persist-client-core'
import { createComputed, createSignal, onCleanup } from 'solid-js'
import {
persistQueryClientRestore,
persistQueryClientSubscribe,
} from '@tanstack/query-persist-client-core'
import { createEffect, createMemo, createSignal, onCleanup } from 'solid-js'
import { IsRestoringProvider, QueryClientProvider } from '@tanstack/solid-query'
import type { PersistQueryClientOptions } from '@tanstack/query-persist-client-core'
import type { QueryClientProviderProps } from '@tanstack/solid-query'
Expand All @@ -15,33 +18,30 @@ export const PersistQueryClientProvider = (
): JSX.Element => {
const [isRestoring, setIsRestoring] = createSignal(true)

let _unsubscribe: undefined | (() => void)
createComputed<() => void>((cleanup) => {
cleanup?.()
let isStale = false
setIsRestoring(true)
const [unsubscribe, promise] = persistQueryClient({
...props.persistOptions,
queryClient: props.client,
})
const options = createMemo(() => ({
...props.persistOptions,
queryClient: props.client,
}))

promise.then(async () => {
if (isStale) return
createEffect(() => {
setIsRestoring(true)
persistQueryClientRestore(options()).then(async () => {
try {
await props.onSuccess?.()
} finally {
setIsRestoring(false)
}
})
})

_unsubscribe = () => {
isStale = true
unsubscribe()
createEffect(() => {
let unsubscribe = () => {}
if (!isRestoring()) {
unsubscribe = persistQueryClientSubscribe(options())
}
return _unsubscribe
onCleanup(() => unsubscribe())
})

onCleanup(() => _unsubscribe?.())
return (
<QueryClientProvider client={props.client}>
<IsRestoringProvider value={isRestoring}>
Expand Down
3 changes: 2 additions & 1 deletion packages/solid-query/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
"test:lib": "vitest --retry=3",
"test:lib:dev": "pnpm run test:lib --watch",
"test:build": "publint --strict",
"build": "tsup"
"build": "tsup",
"build:watch": "tsup --watch"
},
"files": [
"build",
Expand Down
104 changes: 54 additions & 50 deletions packages/solid-query/src/createBaseQuery.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
// Had to disable the lint rule because isServer type is defined as false
// in solid-js/web package. I'll create a GitHub issue with them to see
// why that happens.
Expand All @@ -9,10 +8,8 @@ import {
createMemo,
createResource,
createSignal,
mergeProps,
on,
onCleanup,
untrack,
} from 'solid-js'
import { createStore, reconcile, unwrap } from 'solid-js/store'
import { useQueryClient } from './QueryClientProvider'
Expand Down Expand Up @@ -124,25 +121,21 @@ export function createBaseQuery<
const client = createMemo(() => useQueryClient(queryClient?.()))
const isRestoring = useIsRestoring()

const defaultedOptions = createMemo(() =>
mergeProps(client()?.defaultQueryOptions(options()) || {}, {
get _optimisticResults() {
return isRestoring() ? 'isRestoring' : 'optimistic'
},
structuralSharing: false,
...(isServer && { retry: false, throwOnError: true }),
}),
)
const defaultedOptions = createMemo(() => {
const defaultOptions = client().defaultQueryOptions(options())
defaultOptions._optimisticResults = isRestoring()
? 'isRestoring'
: 'optimistic'
defaultOptions.structuralSharing = false
if (isServer) {
defaultOptions.retry = false
defaultOptions.throwOnError = true
}
return defaultOptions
})

const [observer, setObserver] = createSignal(
new Observer(client(), untrack(defaultedOptions)),
)
// we set the value in a computed because `createMemo`
// returns undefined during transitions
createComputed(
on(client, (c) => setObserver(new Observer(c, defaultedOptions())), {
defer: true,
}),
new Observer(client(), defaultedOptions()),
)

const [state, setState] = createStore<QueryObserverResult<TData, TError>>(
Expand Down Expand Up @@ -177,23 +170,27 @@ export function createBaseQuery<
// exist on the query-core QueryObserverResult type
const reconcileOptions = obs.options.reconcile

setState((store) => {
return reconcileFn(
store,
result,
reconcileOptions === undefined ? false : reconcileOptions,
)
})
// If the query has data we don't suspend but instead mutate the resource
// This could happen when placeholderData/initialData is defined
if (
queryResource()?.data &&
result.data &&
!queryResource.loading &&
isRestoring()
)
if (queryResource()?.data && result.data && !queryResource.loading) {
setState((store) => {
return reconcileFn(
store,
result,
reconcileOptions === undefined ? false : reconcileOptions,
)
})
mutate(state)
else refetch()
} else {
setState((store) => {
return reconcileFn(
store,
result,
reconcileOptions === undefined ? false : reconcileOptions,
)
})
refetch()
}
})()
})
}
Expand All @@ -209,13 +206,14 @@ export function createBaseQuery<
() => {
const obs = observer()
return new Promise((resolve, reject) => {
if (isServer) unsubscribe = createServerSubscriber(resolve, reject)
else if (!unsubscribe && !isRestoring())
if (isServer) {
unsubscribe = createServerSubscriber(resolve, reject)
} else if (!unsubscribe && !isRestoring()) {
unsubscribe = createClientSubscriber()

}
obs.updateResult()

if (!state.isLoading && !isRestoring()) {
if (!state.isLoading) {
const query = obs.getCurrentQuery()
resolve(hydratableObserverResult(query, state))
}
Expand Down Expand Up @@ -275,17 +273,27 @@ export function createBaseQuery<

createComputed(
on(
[isRestoring, observer],
([restoring]) => {
const _unsubscribe = unsubscribe
queueMicrotask(() => _unsubscribe?.())
unsubscribe = null
if (!restoring) refetch()
client,
(c) => {
if (unsubscribe) {
unsubscribe()
}
const newObserver = new Observer(c, defaultedOptions())
unsubscribe = createClientSubscriber()
setObserver(newObserver)
},
{
defer: true,
},
{ defer: true },
),
)

createComputed(() => {
if (!isRestoring()) {
refetch()
}
})

onCleanup(() => {
if (unsubscribe) {
unsubscribe()
Expand All @@ -300,11 +308,7 @@ export function createBaseQuery<
obs.setOptions(opts)
setState(obs.getOptimisticResult(opts))
},
{
// Defer because we don't need to trigger on first render
// This only cares about changes to options after the observer is created
defer: true,
},
{ defer: true },
),
)

Expand Down

0 comments on commit c69a18a

Please sign in to comment.