From 83490d582507fc3190d0524e8135c103436976b9 Mon Sep 17 00:00:00 2001 From: Chris Lucas Date: Wed, 25 Jan 2023 20:46:08 +0100 Subject: [PATCH] make query options a writable store --- .../src/__tests__/CreateQuery.svelte | 21 +++-- .../src/__tests__/createQuery.test.ts | 46 +++++++++- packages/svelte-query/src/createBaseQuery.ts | 91 +++++++++++-------- packages/svelte-query/src/createQuery.ts | 41 ++++++--- packages/svelte-query/src/types.ts | 4 +- 5 files changed, 142 insertions(+), 61 deletions(-) diff --git a/packages/svelte-query/src/__tests__/CreateQuery.svelte b/packages/svelte-query/src/__tests__/CreateQuery.svelte index 6e12574d16..b0a833bbff 100644 --- a/packages/svelte-query/src/__tests__/CreateQuery.svelte +++ b/packages/svelte-query/src/__tests__/CreateQuery.svelte @@ -1,17 +1,18 @@ {#if $query.isLoading} @@ -21,3 +22,9 @@ {:else if $query.isSuccess}

Success

{/if} + + diff --git a/packages/svelte-query/src/__tests__/createQuery.test.ts b/packages/svelte-query/src/__tests__/createQuery.test.ts index 191fea1b23..9e07929298 100644 --- a/packages/svelte-query/src/__tests__/createQuery.test.ts +++ b/packages/svelte-query/src/__tests__/createQuery.test.ts @@ -1,16 +1,20 @@ import { describe, it, expect } from 'vitest' import { render, screen } from '@testing-library/svelte' +import { writable } from 'svelte/store' import CreateQuery from './CreateQuery.svelte' import { sleep } from './utils' +import type { CreateQueryOptions, WritableOrVal } from '../types' describe('createQuery', () => { it('Render and wait for success', async () => { render(CreateQuery, { props: { - queryKey: ['test'], - queryFn: async () => { - await sleep(100) - return 'Success' + options: { + queryKey: ['test'], + queryFn: async () => { + await sleep(100) + return 'Success' + }, }, }, }) @@ -25,4 +29,38 @@ describe('createQuery', () => { expect(screen.queryByText('Loading')).not.toBeInTheDocument() expect(screen.queryByText('Error')).not.toBeInTheDocument() }) + + it('should keep previous data with keepPreviousData option set to true', async () => { + const options: WritableOrVal = writable({ + queryKey: ['test', [1]], + queryFn: async ({ queryKey }) => { + await sleep(100) + const ids = queryKey[1] + if (!ids || !Array.isArray(ids)) return [] + return ids.map((id) => ({ id })) + }, + keepPreviousData: true, + }) + render(CreateQuery, { props: { options } }) + + expect(screen.queryByText('id: 1')).not.toBeInTheDocument() + expect(screen.queryByText('id: 2')).not.toBeInTheDocument() + + await sleep(200) + + expect(screen.queryByText('id: 1')).toBeInTheDocument() + expect(screen.queryByText('id: 2')).not.toBeInTheDocument() + + options.update((o) => ({ ...o, queryKey: ['test', [1, 2]] })) + + await sleep(0) + + expect(screen.queryByText('id: 1')).toBeInTheDocument() + expect(screen.queryByText('id: 2')).not.toBeInTheDocument() + + await sleep(200) + + expect(screen.queryByText('id: 1')).toBeInTheDocument() + expect(screen.queryByText('id: 2')).toBeInTheDocument() + }) }) diff --git a/packages/svelte-query/src/createBaseQuery.ts b/packages/svelte-query/src/createBaseQuery.ts index 401dfc51e2..8ed4bdfa3d 100644 --- a/packages/svelte-query/src/createBaseQuery.ts +++ b/packages/svelte-query/src/createBaseQuery.ts @@ -3,9 +3,19 @@ import { type QueryKey, type QueryObserver, } from '@tanstack/query-core' -import type { CreateBaseQueryOptions, CreateBaseQueryResult } from './types' +import { derived, readable, writable, get, type Writable } from 'svelte/store' +import type { + CreateBaseQueryOptions, + CreateBaseQueryResult, + WritableOrVal, +} from './types' import { useQueryClient } from './useQueryClient' -import { derived, readable } from 'svelte/store' + +function isWritable( + obj: WritableOrVal, +): obj is Writable { + return 'subscribe' in obj +} export function createBaseQuery< TQueryFnData, @@ -14,51 +24,60 @@ export function createBaseQuery< TQueryData, TQueryKey extends QueryKey, >( - options: CreateBaseQueryOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey + options: WritableOrVal< + CreateBaseQueryOptions >, Observer: typeof QueryObserver, ): CreateBaseQueryResult { const queryClient = useQueryClient() - const defaultedOptions = queryClient.defaultQueryOptions(options) - defaultedOptions._optimisticResults = 'optimistic' - let observer = new Observer< + let optionsStore: Writable< + CreateBaseQueryOptions + > + if (isWritable(options)) { + optionsStore = options + } else { + optionsStore = writable(options) + } + + const defaultedOptionsStore = derived(optionsStore, ($options) => { + const defaultedOptions = queryClient.defaultQueryOptions($options) + defaultedOptions._optimisticResults = 'optimistic' + + // Include callbacks in batch renders + if (defaultedOptions.onError) { + defaultedOptions.onError = notifyManager.batchCalls( + defaultedOptions.onError, + ) + } + + if (defaultedOptions.onSuccess) { + defaultedOptions.onSuccess = notifyManager.batchCalls( + defaultedOptions.onSuccess, + ) + } + + if (defaultedOptions.onSettled) { + defaultedOptions.onSettled = notifyManager.batchCalls( + defaultedOptions.onSettled, + ) + } + + return defaultedOptions + }) + + const observer = new Observer< TQueryFnData, TError, TData, TQueryData, TQueryKey - >(queryClient, defaultedOptions) - - // Include callbacks in batch renders - if (defaultedOptions.onError) { - defaultedOptions.onError = notifyManager.batchCalls( - defaultedOptions.onError, - ) - } - - if (defaultedOptions.onSuccess) { - defaultedOptions.onSuccess = notifyManager.batchCalls( - defaultedOptions.onSuccess, - ) - } - - if (defaultedOptions.onSettled) { - defaultedOptions.onSettled = notifyManager.batchCalls( - defaultedOptions.onSettled, - ) - } + >(queryClient, get(defaultedOptionsStore)) - readable(observer).subscribe(($observer) => { - observer = $observer + defaultedOptionsStore.subscribe(($defaultedOptions) => { // Do not notify on updates because of changes in the options because // these changes should already be reflected in the optimistic result. - observer.setOptions(defaultedOptions, { listeners: false }) + observer.setOptions($defaultedOptions, { listeners: false }) }) const result = readable(observer.getCurrentResult(), (set) => { @@ -66,8 +85,8 @@ export function createBaseQuery< }) const { subscribe } = derived(result, ($result) => { - $result = observer.getOptimisticResult(defaultedOptions) - return !defaultedOptions.notifyOnChangeProps + $result = observer.getOptimisticResult(get(defaultedOptionsStore)) + return !get(defaultedOptionsStore).notifyOnChangeProps ? observer.trackResult($result) : $result }) diff --git a/packages/svelte-query/src/createQuery.ts b/packages/svelte-query/src/createQuery.ts index da26283b20..9025383be2 100644 --- a/packages/svelte-query/src/createQuery.ts +++ b/packages/svelte-query/src/createQuery.ts @@ -5,6 +5,7 @@ import type { DefinedCreateQueryResult, CreateQueryOptions, CreateQueryResult, + WritableOrVal, } from './types' export function createQuery< @@ -14,7 +15,7 @@ export function createQuery< TQueryKey extends QueryKey = QueryKey, >( options: Omit< - CreateQueryOptions, + WritableOrVal>, 'initialData' > & { initialData?: () => undefined @@ -28,7 +29,7 @@ export function createQuery< TQueryKey extends QueryKey = QueryKey, >( options: Omit< - CreateQueryOptions, + WritableOrVal>, 'initialData' > & { initialData: TQueryFnData | (() => TQueryFnData) @@ -41,7 +42,9 @@ export function createQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: CreateQueryOptions, + options: WritableOrVal< + CreateQueryOptions + >, ): CreateQueryResult export function createQuery< @@ -52,7 +55,7 @@ export function createQuery< >( queryKey: TQueryKey, options?: Omit< - CreateQueryOptions, + WritableOrVal>, 'queryKey' | 'initialData' > & { initialData?: () => undefined }, ): CreateQueryResult @@ -65,7 +68,7 @@ export function createQuery< >( queryKey: TQueryKey, options?: Omit< - CreateQueryOptions, + WritableOrVal>, 'queryKey' | 'initialData' > & { initialData: TQueryFnData | (() => TQueryFnData) }, ): DefinedCreateQueryResult @@ -78,7 +81,7 @@ export function createQuery< >( queryKey: TQueryKey, options?: Omit< - CreateQueryOptions, + WritableOrVal>, 'queryKey' >, ): CreateQueryResult @@ -92,7 +95,7 @@ export function createQuery< queryKey: TQueryKey, queryFn: QueryFunction, options?: Omit< - CreateQueryOptions, + WritableOrVal>, 'queryKey' | 'queryFn' | 'initialData' > & { initialData?: () => undefined }, ): CreateQueryResult @@ -106,7 +109,7 @@ export function createQuery< queryKey: TQueryKey, queryFn: QueryFunction, options?: Omit< - CreateQueryOptions, + WritableOrVal>, 'queryKey' | 'queryFn' | 'initialData' > & { initialData: TQueryFnData | (() => TQueryFnData) }, ): DefinedCreateQueryResult @@ -120,7 +123,7 @@ export function createQuery< queryKey: TQueryKey, queryFn: QueryFunction, options?: Omit< - CreateQueryOptions, + WritableOrVal>, 'queryKey' | 'queryFn' >, ): CreateQueryResult @@ -131,13 +134,25 @@ export function createQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - arg1: TQueryKey | CreateQueryOptions, + arg1: + | TQueryKey + | WritableOrVal>, arg2?: | QueryFunction - | CreateQueryOptions, - arg3?: CreateQueryOptions, + | WritableOrVal>, + arg3?: WritableOrVal< + CreateQueryOptions + >, ): CreateQueryResult { - const parsedOptions = parseQueryArgs(arg1, arg2, arg3) + const parsedOptions = parseQueryArgs( + arg1 as + | TQueryKey + | CreateQueryOptions, + arg2 as + | QueryFunction + | CreateQueryOptions, + arg3 as CreateQueryOptions, + ) const result = createBaseQuery(parsedOptions, QueryObserver) return result } diff --git a/packages/svelte-query/src/types.ts b/packages/svelte-query/src/types.ts index 853f6c464b..65d9081060 100644 --- a/packages/svelte-query/src/types.ts +++ b/packages/svelte-query/src/types.ts @@ -10,7 +10,9 @@ import type { DefinedQueryObserverResult, } from '@tanstack/query-core' import type { QueryClient } from '@tanstack/query-core' -import type { Readable } from 'svelte/store' +import type { Readable, Writable } from 'svelte/store' + +export type WritableOrVal = T | Writable export interface ContextOptions { /**