@@ -6,10 +6,8 @@ import {
66 noop ,
77 Console ,
88 getStatusProps ,
9- shallowEqual ,
109 Updater ,
1110} from './utils'
12- import { QueryInstance , OnStateUpdateFunction } from './queryInstance'
1311import {
1412 ArrayQueryKey ,
1513 InfiniteQueryConfig ,
@@ -19,7 +17,8 @@ import {
1917 QueryFunction ,
2018 QueryStatus ,
2119} from './types'
22- import { QueryCache } from './queryCache'
20+ import type { QueryCache } from './queryCache'
21+ import { QueryObserver , UpdateListener } from './queryObserver'
2322
2423// TYPES
2524
@@ -39,7 +38,7 @@ export interface QueryState<TResult, TError> {
3938 isError : boolean
4039 isFetched : boolean
4140 isFetching : boolean
42- isFetchingMore ? : IsFetchingMoreValue
41+ isFetchingMore : IsFetchingMoreValue
4342 isIdle : boolean
4443 isLoading : boolean
4544 isStale : boolean
@@ -111,7 +110,7 @@ export class Query<TResult, TError> {
111110 queryKey : ArrayQueryKey
112111 queryHash : string
113112 config : QueryConfig < TResult , TError >
114- instances : QueryInstance < TResult , TError > [ ]
113+ observers : QueryObserver < TResult , TError > [ ]
115114 state : QueryState < TResult , TError >
116115 shouldContinueRetryOnFocus ?: boolean
117116 promise ?: Promise < TResult | undefined >
@@ -131,17 +130,14 @@ export class Query<TResult, TError> {
131130 this . queryKey = init . queryKey
132131 this . queryHash = init . queryHash
133132 this . notifyGlobalListeners = init . notifyGlobalListeners
134- this . instances = [ ]
133+ this . observers = [ ]
135134 this . state = getDefaultState ( init . config )
136135
137136 if ( init . config . infinite ) {
138137 const infiniteConfig = init . config as InfiniteQueryConfig < TResult , TError >
139138 const infiniteData = ( this . state . data as unknown ) as TResult [ ] | undefined
140139
141- if (
142- typeof infiniteData !== 'undefined' &&
143- typeof this . state . canFetchMore === 'undefined'
144- ) {
140+ if ( typeof infiniteData !== 'undefined' ) {
145141 this . fetchMoreVariable = infiniteConfig . getFetchMore (
146142 infiniteData [ infiniteData . length - 1 ] ,
147143 infiniteData
@@ -154,31 +150,39 @@ export class Query<TResult, TError> {
154150 this . pageVariables = [ [ ...this . queryKey ] ]
155151 }
156152 }
157- }
158153
159- private dispatch ( action : Action < TResult , TError > ) : void {
160- const newState = queryReducer ( this . state , action )
154+ // If the query started with data, schedule
155+ // a stale timeout
156+ if ( ! isServer && this . state . data ) {
157+ this . scheduleStaleTimeout ( )
158+
159+ // Simulate a query healing process
160+ this . heal ( )
161161
162- // Only update state if something has changed
163- if ( ! shallowEqual ( this . state , newState ) ) {
164- this . state = newState
165- this . instances . forEach ( d => d . onStateUpdate ( newState , action ) )
166- this . notifyGlobalListeners ( this )
162+ // Schedule for garbage collection in case
163+ // nothing subscribes to this query
164+ this . scheduleGarbageCollection ( )
167165 }
168166 }
169167
168+ setConfig ( config : QueryConfig < TResult , TError > ) : void {
169+ this . config = config
170+ }
171+
172+ private dispatch ( action : Action < TResult , TError > ) : void {
173+ this . state = queryReducer ( this . state , action )
174+ this . observers . forEach ( d => d . onQueryUpdate ( this . state , action ) )
175+ this . notifyGlobalListeners ( this )
176+ }
177+
170178 scheduleStaleTimeout ( ) : void {
171179 if ( isServer ) {
172180 return
173181 }
174182
175183 this . clearStaleTimeout ( )
176184
177- if ( this . state . isStale ) {
178- return
179- }
180-
181- if ( this . config . staleTime === Infinity ) {
185+ if ( this . state . isStale || this . config . staleTime === Infinity ) {
182186 return
183187 }
184188
@@ -190,10 +194,6 @@ export class Query<TResult, TError> {
190194 invalidate ( ) : void {
191195 this . clearStaleTimeout ( )
192196
193- if ( ! this . queryCache . queries [ this . queryHash ] ) {
194- return
195- }
196-
197197 if ( this . state . isStale ) {
198198 return
199199 }
@@ -202,12 +202,12 @@ export class Query<TResult, TError> {
202202 }
203203
204204 scheduleGarbageCollection ( ) : void {
205- this . clearCacheTimeout ( )
206-
207- if ( ! this . queryCache . queries [ this . queryHash ] ) {
205+ if ( isServer ) {
208206 return
209207 }
210208
209+ this . clearCacheTimeout ( )
210+
211211 if ( this . config . cacheTime === Infinity ) {
212212 return
213213 }
@@ -249,9 +249,9 @@ export class Query<TResult, TError> {
249249 delete this . promise
250250 }
251251
252- clearIntervals ( ) : void {
253- this . instances . forEach ( instance => {
254- instance . clearInterval ( )
252+ private clearTimersObservers ( ) : void {
253+ this . observers . forEach ( observer => {
254+ observer . clearRefetchInterval ( )
255255 } )
256256 }
257257
@@ -301,19 +301,57 @@ export class Query<TResult, TError> {
301301 this . clearStaleTimeout ( )
302302 this . clearCacheTimeout ( )
303303 this . clearRetryTimeout ( )
304- this . clearIntervals ( )
304+ this . clearTimersObservers ( )
305305 this . cancel ( )
306306 delete this . queryCache . queries [ this . queryHash ]
307307 this . notifyGlobalListeners ( this )
308308 }
309309
310+ isEnabled ( ) : boolean {
311+ return this . observers . some ( observer => observer . config . enabled )
312+ }
313+
314+ shouldRefetchOnWindowFocus ( ) : boolean {
315+ return (
316+ this . isEnabled ( ) &&
317+ this . state . isStale &&
318+ this . observers . some ( observer => observer . config . refetchOnWindowFocus )
319+ )
320+ }
321+
310322 subscribe (
311- onStateUpdate ?: OnStateUpdateFunction < TResult , TError >
312- ) : QueryInstance < TResult , TError > {
313- const instance = new QueryInstance ( this , onStateUpdate )
314- this . instances . push ( instance )
323+ listener ?: UpdateListener < TResult , TError >
324+ ) : QueryObserver < TResult , TError > {
325+ const observer = new QueryObserver < TResult , TError > ( {
326+ queryCache : this . queryCache ,
327+ queryKey : this . queryKey ,
328+ ...this . config ,
329+ } )
330+
331+ observer . subscribe ( listener )
332+
333+ return observer
334+ }
335+
336+ subscribeObserver ( observer : QueryObserver < TResult , TError > ) : void {
337+ this . observers . push ( observer )
315338 this . heal ( )
316- return instance
339+ }
340+
341+ unsubscribeObserver (
342+ observer : QueryObserver < TResult , TError > ,
343+ preventGC ?: boolean
344+ ) : void {
345+ this . observers = this . observers . filter ( x => x !== observer )
346+
347+ if ( ! this . observers . length ) {
348+ this . cancel ( )
349+
350+ if ( ! preventGC ) {
351+ // Schedule garbage collection
352+ this . scheduleGarbageCollection ( )
353+ }
354+ }
317355 }
318356
319357 // Set up the core fetcher function
@@ -323,7 +361,11 @@ export class Query<TResult, TError> {
323361 ) : Promise < TResult > {
324362 try {
325363 // Perform the query
326- const promiseOrValue = fn ( ...this . config . queryFnParamsFilter ! ( args ) )
364+ const filter = this . config . queryFnParamsFilter
365+ const params = filter ? filter ( args ) : args
366+
367+ // Perform the query
368+ const promiseOrValue = fn ( ...params )
327369
328370 this . cancelPromises = ( ) => ( promiseOrValue as any ) ?. cancel ?.( )
329371
@@ -502,8 +544,10 @@ export class Query<TResult, TError> {
502544 this . cancelled = null
503545
504546 try {
505- // Set up the query refreshing state
506- this . dispatch ( { type : ActionType . Fetch } )
547+ // Set to fetching state if not already in it
548+ if ( ! this . state . isFetching ) {
549+ this . dispatch ( { type : ActionType . Fetch } )
550+ }
507551
508552 // Try to get the data
509553 const data = await this . tryFetchData ( queryFn ! , this . queryKey )
@@ -573,6 +617,7 @@ function getDefaultState<TResult, TError>(
573617 error : null ,
574618 isFetched : false ,
575619 isFetching : initialStatus === QueryStatus . Loading ,
620+ isFetchingMore : false ,
576621 failureCount : 0 ,
577622 isStale,
578623 data : initialData ,
0 commit comments