@tanstack/query-core 4.24.6 → 4.24.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/query-core",
3
- "version": "4.24.6",
3
+ "version": "4.24.10",
4
4
  "description": "The framework agnostic core that powers TanStack Query",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
package/src/hydration.ts CHANGED
@@ -142,10 +142,17 @@ export function hydrate(
142
142
  queries.forEach((dehydratedQuery) => {
143
143
  const query = queryCache.get(dehydratedQuery.queryHash)
144
144
 
145
+ // Reset fetch status to idle in the dehydrated state to avoid
146
+ // query being stuck in fetching state upon hydration
147
+ const dehydratedQueryState = {
148
+ ...dehydratedQuery.state,
149
+ fetchStatus: 'idle' as const,
150
+ }
151
+
145
152
  // Do not hydrate if an existing query exists with newer data
146
153
  if (query) {
147
- if (query.state.dataUpdatedAt < dehydratedQuery.state.dataUpdatedAt) {
148
- query.setState(dehydratedQuery.state)
154
+ if (query.state.dataUpdatedAt < dehydratedQueryState.dataUpdatedAt) {
155
+ query.setState(dehydratedQueryState)
149
156
  }
150
157
  return
151
158
  }
@@ -158,7 +165,7 @@ export function hydrate(
158
165
  queryKey: dehydratedQuery.queryKey,
159
166
  queryHash: dehydratedQuery.queryHash,
160
167
  },
161
- dehydratedQuery.state,
168
+ dehydratedQueryState,
162
169
  )
163
170
  })
164
171
  }
@@ -1,5 +1,5 @@
1
1
  import type { MutationObserver } from './mutationObserver'
2
- import type { MutationOptions } from './types'
2
+ import type { MutationOptions, NotifyEvent } from './types'
3
3
  import type { QueryClient } from './queryClient'
4
4
  import { notifyManager } from './notifyManager'
5
5
  import type { Action, MutationState } from './mutation'
@@ -29,34 +29,34 @@ interface MutationCacheConfig {
29
29
  ) => Promise<unknown> | unknown
30
30
  }
31
31
 
32
- interface NotifyEventMutationAdded {
32
+ interface NotifyEventMutationAdded extends NotifyEvent {
33
33
  type: 'added'
34
34
  mutation: Mutation<any, any, any, any>
35
35
  }
36
- interface NotifyEventMutationRemoved {
36
+ interface NotifyEventMutationRemoved extends NotifyEvent {
37
37
  type: 'removed'
38
38
  mutation: Mutation<any, any, any, any>
39
39
  }
40
40
 
41
- interface NotifyEventMutationObserverAdded {
41
+ interface NotifyEventMutationObserverAdded extends NotifyEvent {
42
42
  type: 'observerAdded'
43
43
  mutation: Mutation<any, any, any, any>
44
44
  observer: MutationObserver<any, any, any>
45
45
  }
46
46
 
47
- interface NotifyEventMutationObserverRemoved {
47
+ interface NotifyEventMutationObserverRemoved extends NotifyEvent {
48
48
  type: 'observerRemoved'
49
49
  mutation: Mutation<any, any, any, any>
50
50
  observer: MutationObserver<any, any, any>
51
51
  }
52
52
 
53
- interface NotifyEventMutationObserverOptionsUpdated {
53
+ interface NotifyEventMutationObserverOptionsUpdated extends NotifyEvent {
54
54
  type: 'observerOptionsUpdated'
55
55
  mutation?: Mutation<any, any, any, any>
56
56
  observer: MutationObserver<any, any, any, any>
57
57
  }
58
58
 
59
- interface NotifyEventMutationUpdated {
59
+ interface NotifyEventMutationUpdated extends NotifyEvent {
60
60
  type: 'updated'
61
61
  mutation: Mutation<any, any, any, any>
62
62
  action: Action<any, any, any, any>
package/src/queryCache.ts CHANGED
@@ -2,7 +2,7 @@ import type { QueryFilters } from './utils'
2
2
  import { hashQueryKeyByOptions, matchQuery, parseFilterArgs } from './utils'
3
3
  import type { Action, QueryState } from './query'
4
4
  import { Query } from './query'
5
- import type { QueryKey, QueryOptions } from './types'
5
+ import type { NotifyEvent, QueryKey, QueryOptions } from './types'
6
6
  import { notifyManager } from './notifyManager'
7
7
  import type { QueryClient } from './queryClient'
8
8
  import { Subscribable } from './subscribable'
@@ -19,40 +19,40 @@ interface QueryHashMap {
19
19
  [hash: string]: Query<any, any, any, any>
20
20
  }
21
21
 
22
- interface NotifyEventQueryAdded {
22
+ interface NotifyEventQueryAdded extends NotifyEvent {
23
23
  type: 'added'
24
24
  query: Query<any, any, any, any>
25
25
  }
26
26
 
27
- interface NotifyEventQueryRemoved {
27
+ interface NotifyEventQueryRemoved extends NotifyEvent {
28
28
  type: 'removed'
29
29
  query: Query<any, any, any, any>
30
30
  }
31
31
 
32
- interface NotifyEventQueryUpdated {
32
+ interface NotifyEventQueryUpdated extends NotifyEvent {
33
33
  type: 'updated'
34
34
  query: Query<any, any, any, any>
35
35
  action: Action<any, any>
36
36
  }
37
37
 
38
- interface NotifyEventQueryObserverAdded {
38
+ interface NotifyEventQueryObserverAdded extends NotifyEvent {
39
39
  type: 'observerAdded'
40
40
  query: Query<any, any, any, any>
41
41
  observer: QueryObserver<any, any, any, any, any>
42
42
  }
43
43
 
44
- interface NotifyEventQueryObserverRemoved {
44
+ interface NotifyEventQueryObserverRemoved extends NotifyEvent {
45
45
  type: 'observerRemoved'
46
46
  query: Query<any, any, any, any>
47
47
  observer: QueryObserver<any, any, any, any, any>
48
48
  }
49
49
 
50
- interface NotifyEventQueryObserverResultsUpdated {
50
+ interface NotifyEventQueryObserverResultsUpdated extends NotifyEvent {
51
51
  type: 'observerResultsUpdated'
52
52
  query: Query<any, any, any, any>
53
53
  }
54
54
 
55
- interface NotifyEventQueryObserverOptionsUpdated {
55
+ interface NotifyEventQueryObserverOptionsUpdated extends NotifyEvent {
56
56
  type: 'observerOptionsUpdated'
57
57
  query: Query<any, any, any, any>
58
58
  observer: QueryObserver<any, any, any, any, any>
@@ -426,4 +426,44 @@ describe('dehydration and rehydration', () => {
426
426
 
427
427
  queryClient.clear()
428
428
  })
429
+
430
+ test('should set the fetchStatus to idle in all cases when dehydrating', async () => {
431
+ const queryCache = new QueryCache()
432
+ const queryClient = createQueryClient({ queryCache })
433
+
434
+ let isInitialFetch = true
435
+ let resolvePromise: (value: unknown) => void = () => undefined
436
+
437
+ const customFetchData = () => {
438
+ const promise = new Promise((resolve) => {
439
+ resolvePromise = resolve
440
+ })
441
+ // Resolve the promise in initial fetch
442
+ // because we are awaiting the query first time
443
+ if (isInitialFetch) {
444
+ resolvePromise('string')
445
+ }
446
+ isInitialFetch = false
447
+ return promise
448
+ }
449
+
450
+ await queryClient.prefetchQuery(['string'], () => customFetchData())
451
+
452
+ queryClient.refetchQueries(['string'])
453
+
454
+ const dehydrated = dehydrate(queryClient)
455
+ resolvePromise('string')
456
+ expect(
457
+ dehydrated.queries.find((q) => q.queryHash === '["string"]')?.state
458
+ .fetchStatus,
459
+ ).toBe('fetching')
460
+ const stringified = JSON.stringify(dehydrated)
461
+
462
+ // ---
463
+ const parsed = JSON.parse(stringified)
464
+ const hydrationCache = new QueryCache()
465
+ const hydrationClient = createQueryClient({ queryCache: hydrationCache })
466
+ hydrate(hydrationClient, parsed)
467
+ expect(hydrationCache.find(['string'])?.state.fetchStatus).toBe('idle')
468
+ })
429
469
  })
package/src/types.ts CHANGED
@@ -726,3 +726,16 @@ export interface CancelOptions {
726
726
  export interface SetDataOptions {
727
727
  updatedAt?: number
728
728
  }
729
+
730
+ export type NotifyEventType =
731
+ | 'added'
732
+ | 'removed'
733
+ | 'updated'
734
+ | 'observerAdded'
735
+ | 'observerRemoved'
736
+ | 'observerResultsUpdated'
737
+ | 'observerOptionsUpdated'
738
+
739
+ export interface NotifyEvent {
740
+ type: NotifyEventType
741
+ }