From d5c30f9295afc3dbc109652a9283cab74e27e2cf Mon Sep 17 00:00:00 2001 From: Yiming Date: Tue, 3 Dec 2024 20:12:33 +0800 Subject: [PATCH] feat(tanstack): return query key and pass in abort signal to fetcher (#1903) --- .../tanstack-query/src/runtime-v5/react.ts | 76 +++++++++++-------- .../tanstack-query/src/runtime-v5/svelte.ts | 25 ++++-- .../tanstack-query/src/runtime-v5/vue.ts | 51 +++++++------ .../tanstack-query/src/runtime/react.ts | 36 +++++---- .../tanstack-query/src/runtime/svelte.ts | 31 +++++--- .../plugins/tanstack-query/src/runtime/vue.ts | 37 ++++----- .../tanstack-query/tests/plugin.test.ts | 21 +++-- 7 files changed, 170 insertions(+), 107 deletions(-) diff --git a/packages/plugins/tanstack-query/src/runtime-v5/react.ts b/packages/plugins/tanstack-query/src/runtime-v5/react.ts index e8017befa..4c8c5ceb6 100644 --- a/packages/plugins/tanstack-query/src/runtime-v5/react.ts +++ b/packages/plugins/tanstack-query/src/runtime-v5/react.ts @@ -68,14 +68,18 @@ export function useModelQuery( fetch?: FetchFn ) { const reqUrl = makeUrl(url, args); - return useQuery({ - queryKey: getQueryKey(model, url, args, { - infinite: false, - optimisticUpdate: options?.optimisticUpdate !== false, - }), - queryFn: () => fetcher(reqUrl, undefined, fetch, false), - ...options, + const queryKey = getQueryKey(model, url, args, { + infinite: false, + optimisticUpdate: options?.optimisticUpdate !== false, }); + return { + queryKey, + ...useQuery({ + queryKey, + queryFn: ({ signal }) => fetcher(reqUrl, { signal }, fetch, false), + ...options, + }), + }; } /** @@ -96,14 +100,18 @@ export function useSuspenseModelQuery( fetch?: FetchFn ) { const reqUrl = makeUrl(url, args); - return useSuspenseQuery({ - queryKey: getQueryKey(model, url, args, { - infinite: false, - optimisticUpdate: options?.optimisticUpdate !== false, - }), - queryFn: () => fetcher(reqUrl, undefined, fetch, false), - ...options, + const queryKey = getQueryKey(model, url, args, { + infinite: false, + optimisticUpdate: options?.optimisticUpdate !== false, }); + return { + queryKey, + ...useSuspenseQuery({ + queryKey, + queryFn: ({ signal }) => fetcher(reqUrl, { signal }, fetch, false), + ...options, + }), + }; } /** @@ -123,14 +131,18 @@ export function useInfiniteModelQuery( options: Omit>, 'queryKey' | 'initialPageParam'>, fetch?: FetchFn ) { - return useInfiniteQuery({ - queryKey: getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }), - queryFn: ({ pageParam }) => { - return fetcher(makeUrl(url, pageParam ?? args), undefined, fetch, false); - }, - initialPageParam: args, - ...options, - }); + const queryKey = getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }); + return { + queryKey, + ...useInfiniteQuery({ + queryKey, + queryFn: ({ pageParam, signal }) => { + return fetcher(makeUrl(url, pageParam ?? args), { signal }, fetch, false); + }, + initialPageParam: args, + ...options, + }), + }; } /** @@ -153,14 +165,18 @@ export function useSuspenseInfiniteModelQuery( >, fetch?: FetchFn ) { - return useSuspenseInfiniteQuery({ - queryKey: getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }), - queryFn: ({ pageParam }) => { - return fetcher(makeUrl(url, pageParam ?? args), undefined, fetch, false); - }, - initialPageParam: args, - ...options, - }); + const queryKey = getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }); + return { + queryKey, + ...useSuspenseInfiniteQuery({ + queryKey, + queryFn: ({ pageParam, signal }) => { + return fetcher(makeUrl(url, pageParam ?? args), { signal }, fetch, false); + }, + initialPageParam: args, + ...options, + }), + }; } /** diff --git a/packages/plugins/tanstack-query/src/runtime-v5/svelte.ts b/packages/plugins/tanstack-query/src/runtime-v5/svelte.ts index 2c554aec9..c09ea89ef 100644 --- a/packages/plugins/tanstack-query/src/runtime-v5/svelte.ts +++ b/packages/plugins/tanstack-query/src/runtime-v5/svelte.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { QueryKey } from '@tanstack/react-query-v5'; import { createInfiniteQuery, createMutation, @@ -8,11 +9,12 @@ import { type CreateQueryOptions, type InfiniteData, type MutationOptions, + type QueryFunction, type StoreOrVal, } from '@tanstack/svelte-query-v5'; import { ModelMeta } from '@zenstackhq/runtime/cross'; import { getContext, setContext } from 'svelte'; -import { Readable, derived } from 'svelte/store'; +import { derived, Readable } from 'svelte/store'; import { APIContext, DEFAULT_QUERY_ENDPOINT, @@ -71,7 +73,8 @@ export function useModelQuery( infinite: false, optimisticUpdate: options?.optimisticUpdate !== false, }); - const queryFn = () => fetcher(reqUrl, undefined, fetch, false); + const queryFn: QueryFunction = ({ signal }) => + fetcher(reqUrl, { signal }, fetch, false); let mergedOpt: any; if (isStore(options)) { @@ -91,7 +94,12 @@ export function useModelQuery( ...options, }; } - return createQuery(mergedOpt); + + const result = createQuery(mergedOpt); + return derived(result, (r) => ({ + queryKey, + ...r, + })); } /** @@ -113,8 +121,8 @@ export function useInfiniteModelQuery( fetch?: FetchFn ) { const queryKey = getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }); - const queryFn = ({ pageParam }: { pageParam: unknown }) => - fetcher(makeUrl(url, pageParam ?? args), undefined, fetch, false); + const queryFn: QueryFunction = ({ pageParam, signal }) => + fetcher(makeUrl(url, pageParam ?? args), { signal }, fetch, false); let mergedOpt: StoreOrVal>>; if ( @@ -140,7 +148,12 @@ export function useInfiniteModelQuery( ...options, }; } - return createInfiniteQuery>(mergedOpt); + + const result = createInfiniteQuery>(mergedOpt); + return derived(result, (r) => ({ + queryKey, + ...r, + })); } function isStore(opt: unknown): opt is Readable { diff --git a/packages/plugins/tanstack-query/src/runtime-v5/vue.ts b/packages/plugins/tanstack-query/src/runtime-v5/vue.ts index f62fd78c9..bef7e26ae 100644 --- a/packages/plugins/tanstack-query/src/runtime-v5/vue.ts +++ b/packages/plugins/tanstack-query/src/runtime-v5/vue.ts @@ -6,7 +6,6 @@ import { useQuery, useQueryClient, type InfiniteData, - type QueryKey, type UseInfiniteQueryOptions, type UseMutationOptions, type UseQueryOptions, @@ -69,24 +68,27 @@ export function useModelQuery( | ComputedRef, 'queryKey'> & ExtraQueryOptions>, fetch?: FetchFn ) { - const queryOptions = computed(() => { - const optionsValue = toValue< - (Omit, 'queryKey'> & ExtraQueryOptions) | undefined - >(options); - return { - queryKey: getQueryKey(model, url, args, { - infinite: false, - optimisticUpdate: optionsValue?.optimisticUpdate !== false, - }), - queryFn: ({ queryKey }: { queryKey: QueryKey }) => { - const [_prefix, _model, _op, args] = queryKey; - const reqUrl = makeUrl(url, toValue(args)); - return fetcher(reqUrl, undefined, fetch, false); - }, - ...optionsValue, - }; + const optionsValue = toValue< + (Omit, 'queryKey'> & ExtraQueryOptions) | undefined + >(options); + const queryKey = getQueryKey(model, url, args, { + infinite: false, + optimisticUpdate: optionsValue?.optimisticUpdate !== false, }); - return useQuery(queryOptions); + const queryOptions = computed, 'queryKey'> & ExtraQueryOptions>( + () => { + return { + queryKey, + queryFn: ({ queryKey, signal }) => { + const [_prefix, _model, _op, args] = queryKey; + const reqUrl = makeUrl(url, toValue(args)); + return fetcher(reqUrl, { signal }, fetch, false); + }, + ...optionsValue, + }; + } + ); + return { queryKey, ...useQuery(queryOptions) }; } /** @@ -113,18 +115,21 @@ export function useInfiniteModelQuery( fetch?: FetchFn ) { // CHECKME: vue-query's `useInfiniteQuery`'s input typing seems wrong - const queryOptions: any = computed(() => ({ - queryKey: getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }), - queryFn: ({ queryKey, pageParam }: { queryKey: QueryKey; pageParam?: unknown }) => { + const queryKey = getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }); + const queryOptions: any = computed< + Omit>, 'queryKey' | 'initialPageParam'> + >(() => ({ + queryKey, + queryFn: ({ queryKey, pageParam, signal }) => { const [_prefix, _model, _op, args] = queryKey; const reqUrl = makeUrl(url, pageParam ?? toValue(args)); - return fetcher(reqUrl, undefined, fetch, false); + return fetcher(reqUrl, { signal }, fetch, false); }, initialPageParam: toValue(args), ...toValue(options), })); - return useInfiniteQuery>(queryOptions); + return { queryKey, ...useInfiniteQuery(queryOptions) }; } /** diff --git a/packages/plugins/tanstack-query/src/runtime/react.ts b/packages/plugins/tanstack-query/src/runtime/react.ts index 30340d6f4..ef7076733 100644 --- a/packages/plugins/tanstack-query/src/runtime/react.ts +++ b/packages/plugins/tanstack-query/src/runtime/react.ts @@ -64,14 +64,18 @@ export function useModelQuery( fetch?: FetchFn ) { const reqUrl = makeUrl(url, args); - return useQuery({ - queryKey: getQueryKey(model, url, args, { - infinite: false, - optimisticUpdate: options?.optimisticUpdate !== false, - }), - queryFn: () => fetcher(reqUrl, undefined, fetch, false), - ...options, + const queryKey = getQueryKey(model, url, args, { + infinite: false, + optimisticUpdate: options?.optimisticUpdate !== false, }); + return { + queryKey, + ...useQuery({ + queryKey, + queryFn: ({ signal }) => fetcher(reqUrl, { signal }, fetch, false), + ...options, + }), + }; } /** @@ -91,13 +95,17 @@ export function useInfiniteModelQuery( options?: Omit, 'queryKey'>, fetch?: FetchFn ) { - return useInfiniteQuery({ - queryKey: getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }), - queryFn: ({ pageParam }) => { - return fetcher(makeUrl(url, pageParam ?? args), undefined, fetch, false); - }, - ...options, - }); + const queryKey = getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }); + return { + queryKey, + ...useInfiniteQuery({ + queryKey, + queryFn: ({ pageParam, signal }) => { + return fetcher(makeUrl(url, pageParam ?? args), { signal }, fetch, false); + }, + ...options, + }), + }; } /** diff --git a/packages/plugins/tanstack-query/src/runtime/svelte.ts b/packages/plugins/tanstack-query/src/runtime/svelte.ts index 54f72cd23..d3277d526 100644 --- a/packages/plugins/tanstack-query/src/runtime/svelte.ts +++ b/packages/plugins/tanstack-query/src/runtime/svelte.ts @@ -10,6 +10,7 @@ import { } from '@tanstack/svelte-query'; import { ModelMeta } from '@zenstackhq/runtime/cross'; import { getContext, setContext } from 'svelte'; +import { derived } from 'svelte/store'; import { APIContext, DEFAULT_QUERY_ENDPOINT, @@ -64,14 +65,19 @@ export function useModelQuery( fetch?: FetchFn ) { const reqUrl = makeUrl(url, args); - return createQuery({ - queryKey: getQueryKey(model, url, args, { - infinite: false, - optimisticUpdate: options?.optimisticUpdate !== false, - }), - queryFn: () => fetcher(reqUrl, undefined, fetch, false), + const queryKey = getQueryKey(model, url, args, { + infinite: false, + optimisticUpdate: options?.optimisticUpdate !== false, + }); + const result = createQuery({ + queryKey, + queryFn: ({ signal }) => fetcher(reqUrl, { signal }, fetch, false), ...options, }); + return derived(result, (r) => ({ + queryKey, + ...r, + })); } /** @@ -91,12 +97,17 @@ export function useInfiniteModelQuery( options?: Omit, 'queryKey'>, fetch?: FetchFn ) { - return createInfiniteQuery({ - queryKey: getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }), - queryFn: ({ pageParam }) => - fetcher(makeUrl(url, pageParam ?? args), undefined, fetch, false), + const queryKey = getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }); + const result = createInfiniteQuery({ + queryKey, + queryFn: ({ pageParam, signal }) => + fetcher(makeUrl(url, pageParam ?? args), { signal }, fetch, false), ...options, }); + return derived(result, (r) => ({ + queryKey, + ...r, + })); } /** diff --git a/packages/plugins/tanstack-query/src/runtime/vue.ts b/packages/plugins/tanstack-query/src/runtime/vue.ts index 5627b830b..ce5ad0693 100644 --- a/packages/plugins/tanstack-query/src/runtime/vue.ts +++ b/packages/plugins/tanstack-query/src/runtime/vue.ts @@ -6,7 +6,6 @@ import { useQuery, useQueryClient, type InfiniteData, - type QueryKey, type UseInfiniteQueryOptions, type UseMutationOptions, type UseQueryOptions, @@ -69,24 +68,25 @@ export function useModelQuery( | ComputedRef, 'queryKey'> & ExtraQueryOptions>, fetch?: FetchFn ) { - const queryOptions = computed(() => { - const optionsValue = toValue< - (Omit, 'queryKey'> & ExtraQueryOptions) | undefined - >(options); + const optionsValue = toValue< + (Omit, 'queryKey'> & ExtraQueryOptions) | undefined + >(options); + const queryKey = getQueryKey(model, url, args, { + infinite: false, + optimisticUpdate: optionsValue?.optimisticUpdate !== false, + }); + const queryOptions = computed, 'queryKey'>>(() => { return { - queryKey: getQueryKey(model, url, args, { - infinite: false, - optimisticUpdate: optionsValue?.optimisticUpdate !== false, - }), - queryFn: ({ queryKey }: { queryKey: QueryKey }) => { + queryKey, + queryFn: ({ queryKey, signal }) => { const [_prefix, _model, _op, args] = queryKey; const reqUrl = makeUrl(url, toValue(args)); - return fetcher(reqUrl, undefined, fetch, false); + return fetcher(reqUrl, { signal }, fetch, false); }, ...optionsValue, }; }); - return useQuery(queryOptions); + return { queryKey, ...useQuery(queryOptions) }; } /** @@ -113,18 +113,21 @@ export function useInfiniteModelQuery( fetch?: FetchFn ) { // CHECKME: vue-query's `useInfiniteQuery`'s input typing seems wrong - const queryOptions: any = computed(() => ({ - queryKey: getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }), - queryFn: ({ queryKey, pageParam }: { queryKey: QueryKey; pageParam?: unknown }) => { + const queryKey = getQueryKey(model, url, args, { infinite: true, optimisticUpdate: false }); + const queryOptions: any = computed< + Omit>, 'queryKey' | 'initialPageParam'> + >(() => ({ + queryKey, + queryFn: ({ queryKey, pageParam, signal }) => { const [_prefix, _model, _op, args] = queryKey; const reqUrl = makeUrl(url, pageParam ?? toValue(args)); - return fetcher(reqUrl, undefined, fetch, false); + return fetcher(reqUrl, { signal }, fetch, false); }, initialPageParam: toValue(args), ...toValue(options), })); - return useInfiniteQuery(queryOptions); + return { queryKey, ...useInfiniteQuery(queryOptions) }; } /** diff --git a/packages/plugins/tanstack-query/tests/plugin.test.ts b/packages/plugins/tanstack-query/tests/plugin.test.ts index 7fc7a18b3..950b0d9a2 100644 --- a/packages/plugins/tanstack-query/tests/plugin.test.ts +++ b/packages/plugins/tanstack-query/tests/plugin.test.ts @@ -54,15 +54,17 @@ model Foo { import { useFindFirstpost_Item, useInfiniteFindManypost_Item, useCreatepost_Item } from './hooks'; function query() { - const { data } = useFindFirstpost_Item({include: { author: true }}, { enabled: true, optimisticUpdate: false }); + const { data, queryKey } = useFindFirstpost_Item({include: { author: true }}, { enabled: true, optimisticUpdate: false }); + console.log(queryKey); console.log(data?.viewCount); console.log(data?.author?.email); } function infiniteQuery() { - const { data, fetchNextPage, hasNextPage } = useInfiniteFindManypost_Item(); + const { data, queryKey, fetchNextPage, hasNextPage } = useInfiniteFindManypost_Item(); useInfiniteFindManypost_Item({ where: { published: true } }); useInfiniteFindManypost_Item(undefined, { enabled: true, getNextPageParam: () => null }); + console.log(queryKey); console.log(data?.pages[0][0].published); console.log(data?.pageParams[0]); } @@ -124,9 +126,10 @@ ${sharedModel} import { useSuspenseInfiniteFindManypost_Item } from './hooks'; function suspenseInfiniteQuery() { - const { data, fetchNextPage, hasNextPage } = useSuspenseInfiniteFindManypost_Item(); + const { data, queryKey, fetchNextPage, hasNextPage } = useSuspenseInfiniteFindManypost_Item(); useSuspenseInfiniteFindManypost_Item({ where: { published: true } }); useSuspenseInfiniteFindManypost_Item(undefined, { getNextPageParam: () => null }); + console.log(queryKey); console.log(data?.pages[0][0].published); console.log(data?.pageParams[0]); } @@ -143,15 +146,17 @@ ${sharedModel} import { useFindFirstpost_Item, useInfiniteFindManypost_Item, useCreatepost_Item } from './hooks'; function query() { - const { data } = useFindFirstpost_Item({include: { author: true }}, { enabled: true, optimisticUpdate: false }); + const { data, queryKey } = useFindFirstpost_Item({include: { author: true }}, { enabled: true, optimisticUpdate: false }); + console.log(queryKey); console.log(data.value?.viewCount); console.log(data.value?.author?.email); } function infiniteQuery() { - const { data, fetchNextPage, hasNextPage } = useInfiniteFindManypost_Item(); + const { data, queryKey, fetchNextPage, hasNextPage } = useInfiniteFindManypost_Item(); useInfiniteFindManypost_Item({ where: { published: true } }, { enabled: true, getNextPageParam: () => null }); useInfiniteFindManypost_Item(undefined, { getNextPageParam: () => null }); + console.log(queryKey); console.log(data.value?.pages[0][0].published); console.log(data.value?.pageParams[0]); } @@ -217,15 +222,17 @@ ${sharedModel} import { useFindFirstpost_Item, useInfiniteFindManypost_Item, useCreatepost_Item } from './hooks'; function query() { - const { data } = get(useFindFirstpost_Item({include: { author: true }}, { enabled: true, optimisticUpdate: false })); + const { data, queryKey } = get(useFindFirstpost_Item({include: { author: true }}, { enabled: true, optimisticUpdate: false })); + console.log(queryKey); console.log(data?.viewCount); console.log(data?.author?.email); } function infiniteQuery() { - const { data, fetchNextPage, hasNextPage } = get(useInfiniteFindManypost_Item()); + const { data, queryKey, fetchNextPage, hasNextPage } = get(useInfiniteFindManypost_Item()); useInfiniteFindManypost_Item({ where: { published: true } }); useInfiniteFindManypost_Item(undefined, { enabled: true, getNextPageParam: () => null }); + console.log(queryKey); console.log(data?.pages[0][0].published); console.log(data?.pageParams[0]); }