Skip to content

Commit 5d05464

Browse files
authored
feat: add QueryCache.watchQuery (#1058)
1 parent 6b15ae0 commit 5d05464

File tree

5 files changed

+211
-88
lines changed

5 files changed

+211
-88
lines changed

src/core/config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ export function getResolvedQueryConfig<TResult, TError>(
119119
return resolvedConfig
120120
}
121121

122+
export function isResolvedQueryConfig<TResult, TError>(
123+
config: any
124+
): config is ResolvedQueryConfig<TResult, TError> {
125+
return Boolean(config.queryHash)
126+
}
127+
122128
export function getResolvedMutationConfig<
123129
TResult,
124130
TError,

src/core/query.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,9 @@ export class Query<TResult, TError> {
284284
this.continue()
285285
}
286286

287+
/**
288+
* @deprectated
289+
*/
287290
subscribe(
288291
listener?: UpdateListener<TResult, TError>
289292
): QueryObserver<TResult, TError> {
@@ -319,6 +322,9 @@ export class Query<TResult, TError> {
319322
}
320323
}
321324

325+
/**
326+
* @deprectated
327+
*/
322328
refetch(
323329
options?: RefetchOptions,
324330
config?: ResolvedQueryConfig<TResult, TError>
@@ -332,6 +338,9 @@ export class Query<TResult, TError> {
332338
return promise
333339
}
334340

341+
/**
342+
* @deprectated
343+
*/
335344
fetchMore(
336345
fetchMoreVariable?: unknown,
337346
options?: FetchMoreOptions,

src/core/queryCache.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
ResolvedQueryConfig,
2121
} from './types'
2222
import { notifyManager } from './notifyManager'
23+
import { QueryObserver } from './queryObserver'
2324

2425
// TYPES
2526

@@ -455,6 +456,36 @@ export class QueryCache {
455456
return promise
456457
}
457458

459+
// Parameter syntax
460+
watchQuery<TResult = unknown, TError = unknown>(
461+
queryKey: QueryKey,
462+
queryConfig?: QueryConfig<TResult, TError>
463+
): QueryObserver<TResult, TError>
464+
465+
// Parameter syntax with query function
466+
watchQuery<TResult, TError, TArgs extends TypedQueryFunctionArgs>(
467+
queryKey: QueryKey,
468+
queryFn: TypedQueryFunction<TResult, TArgs>,
469+
queryConfig?: QueryConfig<TResult, TError>
470+
): QueryObserver<TResult, TError>
471+
472+
watchQuery<TResult = unknown, TError = unknown>(
473+
queryKey: QueryKey,
474+
queryFn: QueryFunction<TResult>,
475+
queryConfig?: QueryConfig<TResult, TError>
476+
): QueryObserver<TResult, TError>
477+
478+
// Implementation
479+
watchQuery<TResult, TError>(
480+
arg1: any,
481+
arg2?: any,
482+
arg3?: any
483+
): QueryObserver<TResult, TError> {
484+
const [queryKey, config] = getQueryArgs<TResult, TError>(arg1, arg2, arg3)
485+
const resolvedConfig = this.getResolvedQueryConfig(queryKey, config)
486+
return new QueryObserver(resolvedConfig)
487+
}
488+
458489
setQueryData<TResult, TError = unknown>(
459490
queryKey: QueryKey,
460491
updater: Updater<TResult | undefined, TResult>,

src/core/queryObserver.ts

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import {
66
noop,
77
} from './utils'
88
import { notifyManager } from './notifyManager'
9-
import type { QueryResult, ResolvedQueryConfig } from './types'
9+
import type { QueryConfig, QueryResult, ResolvedQueryConfig } from './types'
1010
import type { Query, Action, FetchMoreOptions, RefetchOptions } from './query'
11+
import { DEFAULT_CONFIG, isResolvedQueryConfig } from './config'
1112

1213
export type UpdateListener<TResult, TError> = (
1314
result: QueryResult<TResult, TError>
@@ -41,13 +42,14 @@ export class QueryObserver<TResult, TError> {
4142
this.remove = this.remove.bind(this)
4243
this.refetch = this.refetch.bind(this)
4344
this.fetchMore = this.fetchMore.bind(this)
45+
this.unsubscribe = this.unsubscribe.bind(this)
4446

4547
// Subscribe to the query
4648
this.updateQuery()
4749
}
4850

4951
subscribe(listener?: UpdateListener<TResult, TError>): () => void {
50-
this.listener = listener
52+
this.listener = listener || noop
5153
this.currentQuery.subscribeObserver(this)
5254

5355
if (
@@ -60,7 +62,8 @@ export class QueryObserver<TResult, TError> {
6062
}
6163

6264
this.updateTimers()
63-
return this.unsubscribe.bind(this)
65+
66+
return this.unsubscribe
6467
}
6568

6669
unsubscribe(): void {
@@ -69,11 +72,19 @@ export class QueryObserver<TResult, TError> {
6972
this.currentQuery.unsubscribeObserver(this)
7073
}
7174

72-
updateConfig(config: ResolvedQueryConfig<TResult, TError>): void {
75+
updateConfig(
76+
config: QueryConfig<TResult, TError> | ResolvedQueryConfig<TResult, TError>
77+
): void {
7378
const prevConfig = this.config
7479
const prevQuery = this.currentQuery
7580

76-
this.config = config
81+
this.config = isResolvedQueryConfig(config)
82+
? config
83+
: this.config.queryCache.getResolvedQueryConfig(
84+
this.config.queryKey,
85+
config
86+
)
87+
7788
this.updateQuery()
7889

7990
// Take no further actions if there is no subscriber
@@ -143,6 +154,11 @@ export class QueryObserver<TResult, TError> {
143154
}
144155

145156
fetch(): Promise<TResult | undefined> {
157+
// Never try to fetch if no query function has been set
158+
if (this.config.queryFn === DEFAULT_CONFIG.queries?.queryFn) {
159+
return Promise.resolve(this.currentResult.data)
160+
}
161+
146162
return this.currentQuery.fetch(undefined, this.config).catch(noop)
147163
}
148164

@@ -157,50 +173,6 @@ export class QueryObserver<TResult, TError> {
157173
}
158174
}
159175

160-
private notify(options: NotifyOptions): void {
161-
const { config, currentResult, currentQuery, listener } = this
162-
const { onSuccess, onSettled, onError } = config
163-
164-
notifyManager.batch(() => {
165-
// First trigger the configuration callbacks
166-
if (options.onSuccess) {
167-
if (onSuccess) {
168-
notifyManager.schedule(() => {
169-
onSuccess(currentResult.data!)
170-
})
171-
}
172-
if (onSettled) {
173-
notifyManager.schedule(() => {
174-
onSettled(currentResult.data!, null)
175-
})
176-
}
177-
} else if (options.onError) {
178-
if (onError) {
179-
notifyManager.schedule(() => {
180-
onError(currentResult.error!)
181-
})
182-
}
183-
if (onSettled) {
184-
notifyManager.schedule(() => {
185-
onSettled(undefined, currentResult.error!)
186-
})
187-
}
188-
}
189-
190-
// Then trigger the listener
191-
if (options.listener && listener) {
192-
notifyManager.schedule(() => {
193-
listener(currentResult)
194-
})
195-
}
196-
197-
// Then the global listeners
198-
if (options.globalListeners) {
199-
config.queryCache.notifyGlobalListeners(currentQuery)
200-
}
201-
})
202-
}
203-
204176
private updateStaleTimeout(): void {
205177
if (isServer) {
206178
return
@@ -393,4 +365,48 @@ export class QueryObserver<TResult, TError> {
393365

394366
this.notify(notifyOptions)
395367
}
368+
369+
private notify(options: NotifyOptions): void {
370+
const { config, currentResult, currentQuery, listener } = this
371+
const { onSuccess, onSettled, onError } = config
372+
373+
notifyManager.batch(() => {
374+
// First trigger the configuration callbacks
375+
if (options.onSuccess) {
376+
if (onSuccess) {
377+
notifyManager.schedule(() => {
378+
onSuccess(currentResult.data!)
379+
})
380+
}
381+
if (onSettled) {
382+
notifyManager.schedule(() => {
383+
onSettled(currentResult.data!, null)
384+
})
385+
}
386+
} else if (options.onError) {
387+
if (onError) {
388+
notifyManager.schedule(() => {
389+
onError(currentResult.error!)
390+
})
391+
}
392+
if (onSettled) {
393+
notifyManager.schedule(() => {
394+
onSettled(undefined, currentResult.error!)
395+
})
396+
}
397+
}
398+
399+
// Then trigger the listener
400+
if (options.listener && listener) {
401+
notifyManager.schedule(() => {
402+
listener(currentResult)
403+
})
404+
}
405+
406+
// Then the global listeners
407+
if (options.globalListeners) {
408+
config.queryCache.notifyGlobalListeners(currentQuery)
409+
}
410+
})
411+
}
396412
}

0 commit comments

Comments
 (0)