diff --git a/.changeset/thin-bugs-change.md b/.changeset/thin-bugs-change.md new file mode 100644 index 0000000000..efaca4fccb --- /dev/null +++ b/.changeset/thin-bugs-change.md @@ -0,0 +1,5 @@ +--- +'@tanstack/svelte-query': patch +--- + +fix: support async Svelte diff --git a/packages/svelte-query/src/createBaseQuery.svelte.ts b/packages/svelte-query/src/createBaseQuery.svelte.ts index 03fc6b28db..401a471814 100644 --- a/packages/svelte-query/src/createBaseQuery.svelte.ts +++ b/packages/svelte-query/src/createBaseQuery.svelte.ts @@ -1,3 +1,4 @@ +import { onDestroy } from 'svelte' import { useIsRestoring } from './useIsRestoring.js' import { useQueryClient } from './useQueryClient.js' import { createRawRef } from './containers.svelte.js' @@ -71,13 +72,34 @@ export function createBaseQuery< createResult(), ) - $effect(() => { - const unsubscribe = isRestoring.current + // The following is convoluted but necessary: + // Call eagerly so subscription happens on the server and on suspended branches in the client... + let unsubscribe = + isRestoring.current && typeof window !== 'undefined' ? () => undefined : observer.subscribe(() => update(createResult())) - observer.updateResult() - return unsubscribe - }) + // ...but also watch for state changes to resubscribe, and because Svelte right now doesn't + // run onDestroy on components with pending work that are destroyed again before they are resolved... + watchChanges( + () => [isRestoring.current, observer] as const, + 'pre', + () => { + unsubscribe() + unsubscribe = isRestoring.current + ? () => undefined + : observer.subscribe(() => update(createResult())) + observer.updateResult() + return unsubscribe + }, + ) + // ...and finally also cleanup via onDestroy because that one runs on the server whereas $effect.pre does not. + // (in a try-catch because it theoretically can be called in a non-component context - that should not happen + // but it would be a breaking change technically to error out here. SSR-safe because this wouldn't be called during SSR if it was not in a component) + try { + onDestroy(() => { + unsubscribe() + }) + } catch (e) {} watchChanges( () => resolvedOptions,