11import {
2+ isCancelable ,
23 isServer ,
34 functionalUpdate ,
4- cancelledError ,
55 isDocumentVisible ,
66 noop ,
77 Console ,
@@ -104,6 +104,8 @@ export type Action<TResult, TError> =
104104 | SetStateAction < TResult , TError >
105105 | SuccessAction < TResult >
106106
107+ class CancelledError extends Error { }
108+
107109// CLASS
108110
109111export class Query < TResult , TError > {
@@ -122,7 +124,7 @@ export class Query<TResult, TError> {
122124 private retryTimeout ?: number
123125 private staleTimeout ?: number
124126 private cancelPromises ?: ( ) => void
125- private cancelled ?: typeof cancelledError | null
127+ private cancelled ?: boolean
126128 private notifyGlobalListeners : ( query : Query < TResult , TError > ) => void
127129
128130 constructor ( init : QueryInitConfig < TResult , TError > ) {
@@ -157,12 +159,13 @@ export class Query<TResult, TError> {
157159 }
158160
159161 private dispatch ( action : Action < TResult , TError > ) : void {
160- const newState = queryReducer ( this . state , action )
162+ const prevState = this . state
163+ const newState = queryReducer ( prevState , action )
161164
162165 // Only update state if something has changed
163- if ( ! shallowEqual ( this . state , newState ) ) {
166+ if ( ! shallowEqual ( prevState , newState ) ) {
164167 this . state = newState
165- this . instances . forEach ( d => d . onStateUpdate ( newState , action ) )
168+ this . instances . forEach ( d => d . onStateUpdate ( newState , prevState , action ) )
166169 this . notifyGlobalListeners ( this )
167170 }
168171 }
@@ -236,11 +239,15 @@ export class Query<TResult, TError> {
236239 this . clearCacheTimeout ( )
237240
238241 // Mark the query as not cancelled
239- this . cancelled = null
242+ this . cancelled = false
243+ }
244+
245+ canCancel ( ) : boolean {
246+ return ! this . cancelled && Boolean ( this . cancelPromises )
240247 }
241248
242249 cancel ( ) : void {
243- this . cancelled = cancelledError
250+ this . cancelled = true
244251
245252 if ( this . cancelPromises ) {
246253 this . cancelPromises ( )
@@ -322,21 +329,32 @@ export class Query<TResult, TError> {
322329 args : ArrayQueryKey
323330 ) : Promise < TResult > {
324331 try {
332+ const filter = this . config . queryFnParamsFilter
333+ const params = filter ? filter ( args ) : args
334+
325335 // Perform the query
326- const promiseOrValue = fn ( ...this . config . queryFnParamsFilter ! ( args ) )
336+ const promiseOrValue = fn ( ...params )
327337
328- this . cancelPromises = ( ) => ( promiseOrValue as any ) ?. cancel ?.( )
338+ if ( isCancelable ( promiseOrValue ) ) {
339+ this . cancelPromises = ( ) => promiseOrValue . cancel ( )
340+ }
329341
330342 const data = await promiseOrValue
331343 delete this . shouldContinueRetryOnFocus
332344
333345 delete this . cancelPromises
334- if ( this . cancelled ) throw this . cancelled
346+
347+ if ( this . cancelled ) {
348+ throw new CancelledError ( )
349+ }
335350
336351 return data
337352 } catch ( error ) {
338353 delete this . cancelPromises
339- if ( this . cancelled ) throw this . cancelled
354+
355+ if ( this . cancelled ) {
356+ throw new CancelledError ( )
357+ }
340358
341359 // Do we need to retry the request?
342360 if (
@@ -368,14 +386,23 @@ export class Query<TResult, TError> {
368386 return await new Promise ( ( resolve , reject ) => {
369387 // Keep track of the retry timeout
370388 this . retryTimeout = setTimeout ( async ( ) => {
371- if ( this . cancelled ) return reject ( this . cancelled )
389+ if ( this . cancelled ) {
390+ return reject ( new CancelledError ( ) )
391+ }
372392
373393 try {
374394 const data = await this . tryFetchData ( fn , args )
375- if ( this . cancelled ) return reject ( this . cancelled )
395+
396+ if ( this . cancelled ) {
397+ return reject ( new CancelledError ( ) )
398+ }
399+
376400 resolve ( data )
377401 } catch ( error ) {
378- if ( this . cancelled ) return reject ( this . cancelled )
402+ if ( this . cancelled ) {
403+ return reject ( new CancelledError ( ) )
404+ }
405+
379406 reject ( error )
380407 }
381408 } , delay )
@@ -499,7 +526,7 @@ export class Query<TResult, TError> {
499526
500527 this . promise = ( async ( ) => {
501528 // If there are any retries pending for this query, kill them
502- this . cancelled = null
529+ this . cancelled = false
503530
504531 try {
505532 // Set up the query refreshing state
@@ -514,15 +541,17 @@ export class Query<TResult, TError> {
514541
515542 return data
516543 } catch ( error ) {
544+ const cancelled = error instanceof CancelledError
545+
517546 this . dispatch ( {
518547 type : ActionType . Error ,
519- cancelled : error === this . cancelled ,
548+ cancelled,
520549 error,
521550 } )
522551
523552 delete this . promise
524553
525- if ( error !== this . cancelled ) {
554+ if ( ! cancelled ) {
526555 throw error
527556 }
528557
@@ -556,11 +585,31 @@ function getDefaultState<TResult, TError>(
556585
557586 const hasInitialData = typeof initialData !== 'undefined'
558587
559- const isStale =
560- ! config . enabled ||
561- ( typeof config . initialStale === 'function'
562- ? config . initialStale ( )
563- : config . initialStale ?? ! hasInitialData )
588+ // A query is stale by default
589+ let isStale = true
590+
591+ if ( hasInitialData ) {
592+ // When initial data is provided, the query is not stale by default
593+ isStale = false
594+
595+ // Mark the query as stale if initialStale is set to `true`
596+ if ( config . initialStale === true ) {
597+ isStale = true
598+ }
599+
600+ // Mark the query as stale if initialStale is set to a function which returns `true`
601+ if (
602+ typeof config . initialStale === 'function' &&
603+ config . initialStale ( ) === true
604+ ) {
605+ isStale = true
606+ }
607+ }
608+
609+ // Always mark the query as stale when it is not enabled
610+ if ( ! config . enabled ) {
611+ isStale = true
612+ }
564613
565614 const initialStatus = hasInitialData
566615 ? QueryStatus . Success
0 commit comments