Skip to content

Commit 83490d5

Browse files
committed
make query options a writable store
1 parent ea67377 commit 83490d5

File tree

5 files changed

+142
-61
lines changed

5 files changed

+142
-61
lines changed
Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
<script lang="ts">
2-
import { createQuery, QueryClient } from '../index'
2+
import {
3+
createQuery,
4+
QueryClient,
5+
type CreateQueryOptions,
6+
type WritableOrVal,
7+
} from '../index'
38
import { setQueryClientContext } from '../context'
49
5-
export let queryKey: Array<string>
6-
export let queryFn: () => Promise<string>
10+
export let options: WritableOrVal<CreateQueryOptions>
711
812
const queryClient = new QueryClient()
913
setQueryClientContext(queryClient)
1014
11-
const query = createQuery({
12-
queryKey,
13-
queryFn,
14-
})
15+
const query = createQuery(options)
1516
</script>
1617

1718
{#if $query.isLoading}
@@ -21,3 +22,9 @@
2122
{:else if $query.isSuccess}
2223
<p>Success</p>
2324
{/if}
25+
26+
<ul>
27+
{#each $query.data ?? [] as entry}
28+
<li>id: {entry.id}</li>
29+
{/each}
30+
</ul>

packages/svelte-query/src/__tests__/createQuery.test.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import { describe, it, expect } from 'vitest'
22
import { render, screen } from '@testing-library/svelte'
3+
import { writable } from 'svelte/store'
34
import CreateQuery from './CreateQuery.svelte'
45
import { sleep } from './utils'
6+
import type { CreateQueryOptions, WritableOrVal } from '../types'
57

68
describe('createQuery', () => {
79
it('Render and wait for success', async () => {
810
render(CreateQuery, {
911
props: {
10-
queryKey: ['test'],
11-
queryFn: async () => {
12-
await sleep(100)
13-
return 'Success'
12+
options: {
13+
queryKey: ['test'],
14+
queryFn: async () => {
15+
await sleep(100)
16+
return 'Success'
17+
},
1418
},
1519
},
1620
})
@@ -25,4 +29,38 @@ describe('createQuery', () => {
2529
expect(screen.queryByText('Loading')).not.toBeInTheDocument()
2630
expect(screen.queryByText('Error')).not.toBeInTheDocument()
2731
})
32+
33+
it('should keep previous data with keepPreviousData option set to true', async () => {
34+
const options: WritableOrVal<CreateQueryOptions> = writable({
35+
queryKey: ['test', [1]],
36+
queryFn: async ({ queryKey }) => {
37+
await sleep(100)
38+
const ids = queryKey[1]
39+
if (!ids || !Array.isArray(ids)) return []
40+
return ids.map((id) => ({ id }))
41+
},
42+
keepPreviousData: true,
43+
})
44+
render(CreateQuery, { props: { options } })
45+
46+
expect(screen.queryByText('id: 1')).not.toBeInTheDocument()
47+
expect(screen.queryByText('id: 2')).not.toBeInTheDocument()
48+
49+
await sleep(200)
50+
51+
expect(screen.queryByText('id: 1')).toBeInTheDocument()
52+
expect(screen.queryByText('id: 2')).not.toBeInTheDocument()
53+
54+
options.update((o) => ({ ...o, queryKey: ['test', [1, 2]] }))
55+
56+
await sleep(0)
57+
58+
expect(screen.queryByText('id: 1')).toBeInTheDocument()
59+
expect(screen.queryByText('id: 2')).not.toBeInTheDocument()
60+
61+
await sleep(200)
62+
63+
expect(screen.queryByText('id: 1')).toBeInTheDocument()
64+
expect(screen.queryByText('id: 2')).toBeInTheDocument()
65+
})
2866
})

packages/svelte-query/src/createBaseQuery.ts

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,19 @@ import {
33
type QueryKey,
44
type QueryObserver,
55
} from '@tanstack/query-core'
6-
import type { CreateBaseQueryOptions, CreateBaseQueryResult } from './types'
6+
import { derived, readable, writable, get, type Writable } from 'svelte/store'
7+
import type {
8+
CreateBaseQueryOptions,
9+
CreateBaseQueryResult,
10+
WritableOrVal,
11+
} from './types'
712
import { useQueryClient } from './useQueryClient'
8-
import { derived, readable } from 'svelte/store'
13+
14+
function isWritable<T extends object>(
15+
obj: WritableOrVal<T>,
16+
): obj is Writable<T> {
17+
return 'subscribe' in obj
18+
}
919

1020
export function createBaseQuery<
1121
TQueryFnData,
@@ -14,60 +24,69 @@ export function createBaseQuery<
1424
TQueryData,
1525
TQueryKey extends QueryKey,
1626
>(
17-
options: CreateBaseQueryOptions<
18-
TQueryFnData,
19-
TError,
20-
TData,
21-
TQueryData,
22-
TQueryKey
27+
options: WritableOrVal<
28+
CreateBaseQueryOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>
2329
>,
2430
Observer: typeof QueryObserver,
2531
): CreateBaseQueryResult<TData, TError> {
2632
const queryClient = useQueryClient()
27-
const defaultedOptions = queryClient.defaultQueryOptions(options)
28-
defaultedOptions._optimisticResults = 'optimistic'
2933

30-
let observer = new Observer<
34+
let optionsStore: Writable<
35+
CreateBaseQueryOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>
36+
>
37+
if (isWritable(options)) {
38+
optionsStore = options
39+
} else {
40+
optionsStore = writable(options)
41+
}
42+
43+
const defaultedOptionsStore = derived(optionsStore, ($options) => {
44+
const defaultedOptions = queryClient.defaultQueryOptions($options)
45+
defaultedOptions._optimisticResults = 'optimistic'
46+
47+
// Include callbacks in batch renders
48+
if (defaultedOptions.onError) {
49+
defaultedOptions.onError = notifyManager.batchCalls(
50+
defaultedOptions.onError,
51+
)
52+
}
53+
54+
if (defaultedOptions.onSuccess) {
55+
defaultedOptions.onSuccess = notifyManager.batchCalls(
56+
defaultedOptions.onSuccess,
57+
)
58+
}
59+
60+
if (defaultedOptions.onSettled) {
61+
defaultedOptions.onSettled = notifyManager.batchCalls(
62+
defaultedOptions.onSettled,
63+
)
64+
}
65+
66+
return defaultedOptions
67+
})
68+
69+
const observer = new Observer<
3170
TQueryFnData,
3271
TError,
3372
TData,
3473
TQueryData,
3574
TQueryKey
36-
>(queryClient, defaultedOptions)
37-
38-
// Include callbacks in batch renders
39-
if (defaultedOptions.onError) {
40-
defaultedOptions.onError = notifyManager.batchCalls(
41-
defaultedOptions.onError,
42-
)
43-
}
44-
45-
if (defaultedOptions.onSuccess) {
46-
defaultedOptions.onSuccess = notifyManager.batchCalls(
47-
defaultedOptions.onSuccess,
48-
)
49-
}
50-
51-
if (defaultedOptions.onSettled) {
52-
defaultedOptions.onSettled = notifyManager.batchCalls(
53-
defaultedOptions.onSettled,
54-
)
55-
}
75+
>(queryClient, get(defaultedOptionsStore))
5676

57-
readable(observer).subscribe(($observer) => {
58-
observer = $observer
77+
defaultedOptionsStore.subscribe(($defaultedOptions) => {
5978
// Do not notify on updates because of changes in the options because
6079
// these changes should already be reflected in the optimistic result.
61-
observer.setOptions(defaultedOptions, { listeners: false })
80+
observer.setOptions($defaultedOptions, { listeners: false })
6281
})
6382

6483
const result = readable(observer.getCurrentResult(), (set) => {
6584
return observer.subscribe(notifyManager.batchCalls(set))
6685
})
6786

6887
const { subscribe } = derived(result, ($result) => {
69-
$result = observer.getOptimisticResult(defaultedOptions)
70-
return !defaultedOptions.notifyOnChangeProps
88+
$result = observer.getOptimisticResult(get(defaultedOptionsStore))
89+
return !get(defaultedOptionsStore).notifyOnChangeProps
7190
? observer.trackResult($result)
7291
: $result
7392
})

packages/svelte-query/src/createQuery.ts

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
DefinedCreateQueryResult,
66
CreateQueryOptions,
77
CreateQueryResult,
8+
WritableOrVal,
89
} from './types'
910

1011
export function createQuery<
@@ -14,7 +15,7 @@ export function createQuery<
1415
TQueryKey extends QueryKey = QueryKey,
1516
>(
1617
options: Omit<
17-
CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
18+
WritableOrVal<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>>,
1819
'initialData'
1920
> & {
2021
initialData?: () => undefined
@@ -28,7 +29,7 @@ export function createQuery<
2829
TQueryKey extends QueryKey = QueryKey,
2930
>(
3031
options: Omit<
31-
CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
32+
WritableOrVal<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>>,
3233
'initialData'
3334
> & {
3435
initialData: TQueryFnData | (() => TQueryFnData)
@@ -41,7 +42,9 @@ export function createQuery<
4142
TData = TQueryFnData,
4243
TQueryKey extends QueryKey = QueryKey,
4344
>(
44-
options: CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
45+
options: WritableOrVal<
46+
CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>
47+
>,
4548
): CreateQueryResult<TData, TError>
4649

4750
export function createQuery<
@@ -52,7 +55,7 @@ export function createQuery<
5255
>(
5356
queryKey: TQueryKey,
5457
options?: Omit<
55-
CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
58+
WritableOrVal<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>>,
5659
'queryKey' | 'initialData'
5760
> & { initialData?: () => undefined },
5861
): CreateQueryResult<TData, TError>
@@ -65,7 +68,7 @@ export function createQuery<
6568
>(
6669
queryKey: TQueryKey,
6770
options?: Omit<
68-
CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
71+
WritableOrVal<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>>,
6972
'queryKey' | 'initialData'
7073
> & { initialData: TQueryFnData | (() => TQueryFnData) },
7174
): DefinedCreateQueryResult<TData, TError>
@@ -78,7 +81,7 @@ export function createQuery<
7881
>(
7982
queryKey: TQueryKey,
8083
options?: Omit<
81-
CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
84+
WritableOrVal<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>>,
8285
'queryKey'
8386
>,
8487
): CreateQueryResult<TData, TError>
@@ -92,7 +95,7 @@ export function createQuery<
9295
queryKey: TQueryKey,
9396
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
9497
options?: Omit<
95-
CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
98+
WritableOrVal<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>>,
9699
'queryKey' | 'queryFn' | 'initialData'
97100
> & { initialData?: () => undefined },
98101
): CreateQueryResult<TData, TError>
@@ -106,7 +109,7 @@ export function createQuery<
106109
queryKey: TQueryKey,
107110
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
108111
options?: Omit<
109-
CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
112+
WritableOrVal<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>>,
110113
'queryKey' | 'queryFn' | 'initialData'
111114
> & { initialData: TQueryFnData | (() => TQueryFnData) },
112115
): DefinedCreateQueryResult<TData, TError>
@@ -120,7 +123,7 @@ export function createQuery<
120123
queryKey: TQueryKey,
121124
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
122125
options?: Omit<
123-
CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
126+
WritableOrVal<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>>,
124127
'queryKey' | 'queryFn'
125128
>,
126129
): CreateQueryResult<TData, TError>
@@ -131,13 +134,25 @@ export function createQuery<
131134
TData = TQueryFnData,
132135
TQueryKey extends QueryKey = QueryKey,
133136
>(
134-
arg1: TQueryKey | CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
137+
arg1:
138+
| TQueryKey
139+
| WritableOrVal<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>>,
135140
arg2?:
136141
| QueryFunction<TQueryFnData, TQueryKey>
137-
| CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
138-
arg3?: CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
142+
| WritableOrVal<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>>,
143+
arg3?: WritableOrVal<
144+
CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>
145+
>,
139146
): CreateQueryResult<TData, TError> {
140-
const parsedOptions = parseQueryArgs(arg1, arg2, arg3)
147+
const parsedOptions = parseQueryArgs(
148+
arg1 as
149+
| TQueryKey
150+
| CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
151+
arg2 as
152+
| QueryFunction<TQueryFnData, TQueryKey>
153+
| CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
154+
arg3 as CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
155+
)
141156
const result = createBaseQuery(parsedOptions, QueryObserver)
142157
return result
143158
}

packages/svelte-query/src/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import type {
1010
DefinedQueryObserverResult,
1111
} from '@tanstack/query-core'
1212
import type { QueryClient } from '@tanstack/query-core'
13-
import type { Readable } from 'svelte/store'
13+
import type { Readable, Writable } from 'svelte/store'
14+
15+
export type WritableOrVal<T> = T | Writable<T>
1416

1517
export interface ContextOptions {
1618
/**

0 commit comments

Comments
 (0)