diff --git a/packages/svelte-query/src/createBaseQuery.svelte.ts b/packages/svelte-query/src/createBaseQuery.svelte.ts index 8307f5e40f..03fc6b28db 100644 --- a/packages/svelte-query/src/createBaseQuery.svelte.ts +++ b/packages/svelte-query/src/createBaseQuery.svelte.ts @@ -1,7 +1,7 @@ -import { untrack } from 'svelte' import { useIsRestoring } from './useIsRestoring.js' import { useQueryClient } from './useQueryClient.js' import { createRawRef } from './containers.svelte.js' +import { watchChanges } from './utils.svelte.js' import type { QueryClient, QueryKey, QueryObserver } from '@tanstack/query-core' import type { Accessor, @@ -39,12 +39,26 @@ export function createBaseQuery< }) /** Creates the observer */ - const observer = $derived( + // svelte-ignore state_referenced_locally - intentional, initial value + let observer = $state( new Observer( client, - untrack(() => resolvedOptions), + resolvedOptions, ), ) + watchChanges( + () => client, + 'pre', + () => { + observer = new Observer< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >(client, resolvedOptions) + }, + ) function createResult() { const result = observer.getOptimisticResult(resolvedOptions) @@ -65,19 +79,29 @@ export function createBaseQuery< return unsubscribe }) - $effect.pre(() => { - observer.setOptions(resolvedOptions) - // The only reason this is necessary is because of `isRestoring`. - // Because we don't subscribe while restoring, the following can occur: - // - `isRestoring` is true - // - `isRestoring` becomes false - // - `observer.subscribe` and `observer.updateResult` is called in the above effect, - // but the subsequent `fetch` has already completed - // - `result` misses the intermediate restored-but-not-fetched state - // - // this could technically be its own effect but that doesn't seem necessary - update(createResult()) - }) + watchChanges( + () => resolvedOptions, + 'pre', + () => { + observer.setOptions(resolvedOptions) + }, + ) + watchChanges( + () => [resolvedOptions, observer], + 'pre', + () => { + // The only reason this is necessary is because of `isRestoring`. + // Because we don't subscribe while restoring, the following can occur: + // - `isRestoring` is true + // - `isRestoring` becomes false + // - `observer.subscribe` and `observer.updateResult` is called in the above effect, + // but the subsequent `fetch` has already completed + // - `result` misses the intermediate restored-but-not-fetched state + // + // this could technically be its own effect but that doesn't seem necessary + update(createResult()) + }, + ) return query } diff --git a/packages/svelte-query/src/createMutation.svelte.ts b/packages/svelte-query/src/createMutation.svelte.ts index 87ae521570..51ff74827a 100644 --- a/packages/svelte-query/src/createMutation.svelte.ts +++ b/packages/svelte-query/src/createMutation.svelte.ts @@ -1,7 +1,6 @@ -import { onDestroy } from 'svelte' - import { MutationObserver, noop, notifyManager } from '@tanstack/query-core' import { useQueryClient } from './useQueryClient.js' +import { watchChanges } from './utils.svelte.js' import type { Accessor, CreateMutateFunction, @@ -24,48 +23,69 @@ export function createMutation< options: Accessor>, queryClient?: Accessor, ): CreateMutationResult { - const client = useQueryClient(queryClient?.()) + const client = $derived(useQueryClient(queryClient?.())) - const observer = $derived( + // svelte-ignore state_referenced_locally - intentional, initial value + let observer = $state( + // svelte-ignore state_referenced_locally - intentional, initial value new MutationObserver( client, options(), ), ) - const mutate = $state< - CreateMutateFunction - >((variables, mutateOptions) => { - observer.mutate(variables, mutateOptions).catch(noop) - }) + watchChanges( + () => client, + 'pre', + () => { + observer = new MutationObserver(client, options()) + }, + ) $effect.pre(() => { observer.setOptions(options()) }) - const result = $state(observer.getCurrentResult()) - - const unsubscribe = observer.subscribe((val) => { - notifyManager.batchCalls(() => { - Object.assign(result, val) - })() + const mutate = >(( + variables, + mutateOptions, + ) => { + observer.mutate(variables, mutateOptions).catch(noop) }) - onDestroy(() => { - unsubscribe() + let result = $state(observer.getCurrentResult()) + watchChanges( + () => observer, + 'pre', + () => { + result = observer.getCurrentResult() + }, + ) + + $effect.pre(() => { + const unsubscribe = observer.subscribe((val) => { + notifyManager.batchCalls(() => { + Object.assign(result, val) + })() + }) + return unsubscribe }) + const resultProxy = $derived( + new Proxy(result, { + get: (_, prop) => { + const r = { + ...result, + mutate, + mutateAsync: result.mutate, + } + if (prop == 'value') return r + // @ts-expect-error + return r[prop] + }, + }), + ) + // @ts-expect-error - return new Proxy(result, { - get: (_, prop) => { - const r = { - ...result, - mutate, - mutateAsync: result.mutate, - } - if (prop == 'value') return r - // @ts-expect-error - return r[prop] - }, - }) + return resultProxy } diff --git a/packages/svelte-query/src/createQueries.svelte.ts b/packages/svelte-query/src/createQueries.svelte.ts index e546dc600d..dec5756129 100644 --- a/packages/svelte-query/src/createQueries.svelte.ts +++ b/packages/svelte-query/src/createQueries.svelte.ts @@ -1,5 +1,4 @@ import { QueriesObserver } from '@tanstack/query-core' -import { untrack } from 'svelte' import { useIsRestoring } from './useIsRestoring.js' import { createRawRef } from './containers.svelte.js' import { useQueryClient } from './useQueryClient.js' @@ -216,11 +215,12 @@ export function createQueries< }), ) + // can't do same as createMutation, as QueriesObserver has no `setOptions` method const observer = $derived( new QueriesObserver( client, - untrack(() => resolvedQueryOptions), - untrack(() => combine as QueriesObserverOptions), + resolvedQueryOptions, + combine as QueriesObserverOptions, ), ) diff --git a/packages/svelte-query/src/utils.svelte.ts b/packages/svelte-query/src/utils.svelte.ts new file mode 100644 index 0000000000..9e8073aab7 --- /dev/null +++ b/packages/svelte-query/src/utils.svelte.ts @@ -0,0 +1,44 @@ +import { untrack } from 'svelte' +// modified from the great https://github.com/svecosystem/runed +function runEffect( + flush: 'post' | 'pre', + effect: () => void | VoidFunction, +): void { + switch (flush) { + case 'post': + $effect(effect) + break + case 'pre': + $effect.pre(effect) + break + } +} +type Getter = () => T +export const watchChanges = ( + sources: Getter | Array>, + flush: 'post' | 'pre', + effect: ( + values: T | Array, + previousValues: T | undefined | Array, + ) => void, +) => { + let active = false + let previousValues: T | undefined | Array = Array.isArray( + sources, + ) + ? [] + : undefined + runEffect(flush, () => { + const values = Array.isArray(sources) + ? sources.map((source) => source()) + : sources() + if (!active) { + active = true + previousValues = values + return + } + const cleanup = untrack(() => effect(values, previousValues)) + previousValues = values + return cleanup + }) +} diff --git a/packages/svelte-query/tests/ProviderWrapper.svelte b/packages/svelte-query/tests/ProviderWrapper.svelte new file mode 100644 index 0000000000..b61d2d99da --- /dev/null +++ b/packages/svelte-query/tests/ProviderWrapper.svelte @@ -0,0 +1,14 @@ + + + + {@render children()} + diff --git a/packages/svelte-query/tests/createQuery.svelte.test.ts b/packages/svelte-query/tests/createQuery.svelte.test.ts index bf2c478536..9cec8a17b5 100644 --- a/packages/svelte-query/tests/createQuery.svelte.test.ts +++ b/packages/svelte-query/tests/createQuery.svelte.test.ts @@ -1890,4 +1890,31 @@ describe('createQuery', () => { expect(query.error?.message).toBe('Local Error') }), ) + + it( + 'should support changing provided query client', + withEffectRoot(async () => { + const queryClient1 = new QueryClient() + const queryClient2 = new QueryClient() + + let queryClient = $state(queryClient1) + + const key = ['test'] + + createQuery( + () => ({ + queryKey: key, + queryFn: () => Promise.resolve('prefetched'), + }), + () => queryClient, + ) + + expect(queryClient1.getQueryCache().find({ queryKey: key })).toBeDefined() + + queryClient = queryClient2 + flushSync() + + expect(queryClient2.getQueryCache().find({ queryKey: key })).toBeDefined() + }), + ) }) diff --git a/packages/svelte-query/tests/useIsFetching/BaseExample.svelte b/packages/svelte-query/tests/useIsFetching/BaseExample.svelte index 00c8f9c2b8..522955c79b 100644 --- a/packages/svelte-query/tests/useIsFetching/BaseExample.svelte +++ b/packages/svelte-query/tests/useIsFetching/BaseExample.svelte @@ -1,24 +1,11 @@ - + + -
isFetching: {isFetching.current}
-
Data: {query.data ?? 'undefined'}
+ +
diff --git a/packages/svelte-query/tests/useIsFetching/FetchStatus.svelte b/packages/svelte-query/tests/useIsFetching/FetchStatus.svelte new file mode 100644 index 0000000000..5b10705709 --- /dev/null +++ b/packages/svelte-query/tests/useIsFetching/FetchStatus.svelte @@ -0,0 +1,6 @@ + + +
isFetching: {isFetching.current}
diff --git a/packages/svelte-query/tests/useIsFetching/Query.svelte b/packages/svelte-query/tests/useIsFetching/Query.svelte new file mode 100644 index 0000000000..3a2eeb669e --- /dev/null +++ b/packages/svelte-query/tests/useIsFetching/Query.svelte @@ -0,0 +1,19 @@ + + + + +
Data: {query.data ?? 'undefined'}
diff --git a/packages/svelte-query/tests/useIsMutating/BaseExample.svelte b/packages/svelte-query/tests/useIsMutating/BaseExample.svelte index de89cc9867..e2e46622c7 100644 --- a/packages/svelte-query/tests/useIsMutating/BaseExample.svelte +++ b/packages/svelte-query/tests/useIsMutating/BaseExample.svelte @@ -1,20 +1,11 @@ - + + -
isMutating: {isMutating.current}
+ +
diff --git a/packages/svelte-query/tests/useIsMutating/MutatingStatus.svelte b/packages/svelte-query/tests/useIsMutating/MutatingStatus.svelte new file mode 100644 index 0000000000..a747ed8326 --- /dev/null +++ b/packages/svelte-query/tests/useIsMutating/MutatingStatus.svelte @@ -0,0 +1,6 @@ + + +
isMutating: {isMutating.current}
diff --git a/packages/svelte-query/tests/useIsMutating/Query.svelte b/packages/svelte-query/tests/useIsMutating/Query.svelte new file mode 100644 index 0000000000..f9cc2504b0 --- /dev/null +++ b/packages/svelte-query/tests/useIsMutating/Query.svelte @@ -0,0 +1,14 @@ + + +