Skip to content

Commit fb02eb8

Browse files
committed
fix: notify query cache on stale
1 parent 1eb4d00 commit fb02eb8

File tree

10 files changed

+66
-38
lines changed

10 files changed

+66
-38
lines changed

src/core/config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ export const DEFAULT_CONFIG: ReactQueryConfig = {
6666
},
6767
}
6868

69+
export function getDefaultReactQueryConfig() {
70+
return {
71+
shared: { ...DEFAULT_CONFIG.shared },
72+
queries: { ...DEFAULT_CONFIG.queries },
73+
mutations: { ...DEFAULT_CONFIG.mutations },
74+
}
75+
}
76+
6977
export function mergeReactQueryConfigs(
7078
a: ReactQueryConfig,
7179
b: ReactQueryConfig

src/core/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export { getDefaultReactQueryConfig } from './config'
12
export { queryCache, queryCaches, makeQueryCache } from './queryCache'
23
export { setFocusHandler } from './setFocusHandler'
34
export { setOnlineHandler } from './setOnlineHandler'

src/core/query.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,16 @@ export interface QueryState<TResult, TError> {
3232
data?: TResult
3333
error: TError | null
3434
failureCount: number
35-
fetchedCount: number
3635
isError: boolean
37-
isFetched: boolean
3836
isFetching: boolean
3937
isFetchingMore: IsFetchingMoreValue
4038
isIdle: boolean
39+
isInitialData: boolean
4140
isLoading: boolean
4241
isSuccess: boolean
4342
status: QueryStatus
4443
throwInErrorBoundary?: boolean
44+
updateCount: number
4545
updatedAt: number
4646
}
4747

@@ -593,15 +593,15 @@ function getDefaultState<TResult, TError>(
593593

594594
return {
595595
...getStatusProps(initialStatus),
596+
canFetchMore: hasMorePages(config, initialData),
597+
data: initialData,
596598
error: null,
597-
isFetched: Boolean(config.initialFetched),
599+
failureCount: 0,
598600
isFetching: initialStatus === QueryStatus.Loading,
599601
isFetchingMore: false,
600-
failureCount: 0,
601-
fetchedCount: config.initialFetched ? 1 : 0,
602-
data: initialData,
602+
isInitialData: true,
603+
updateCount: 0,
603604
updatedAt: Date.now(),
604-
canFetchMore: hasMorePages(config, initialData),
605605
}
606606
}
607607

@@ -631,27 +631,26 @@ export function queryReducer<TResult, TError>(
631631
return {
632632
...state,
633633
...getStatusProps(QueryStatus.Success),
634+
canFetchMore: action.canFetchMore,
634635
data: action.data,
635636
error: null,
636-
fetchedCount: state.fetchedCount + 1,
637-
isFetched: true,
637+
failureCount: 0,
638638
isFetching: false,
639639
isFetchingMore: false,
640-
canFetchMore: action.canFetchMore,
640+
isInitialData: false,
641+
updateCount: state.updateCount + 1,
641642
updatedAt: action.updatedAt ?? Date.now(),
642-
failureCount: 0,
643643
}
644644
case ActionType.Error:
645645
return {
646646
...state,
647647
...getStatusProps(QueryStatus.Error),
648648
error: action.error,
649-
fetchedCount: state.fetchedCount + 1,
650-
isFetched: true,
649+
failureCount: state.failureCount + 1,
651650
isFetching: false,
652651
isFetchingMore: false,
653-
failureCount: state.failureCount + 1,
654652
throwInErrorBoundary: true,
653+
updateCount: state.updateCount + 1,
655654
}
656655
default:
657656
return state

src/core/queryCache.ts

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
Updater,
33
deepIncludes,
4-
functionalUpdate,
54
getQueryArgs,
65
isDocumentVisible,
76
isPlainObject,
@@ -336,19 +335,7 @@ export class QueryCache {
336335
updater: Updater<TResult | undefined, TResult>,
337336
config?: QueryConfig<TResult, TError>
338337
) {
339-
const resolvedConfig = this.getResolvedQueryConfig(queryKey, config)
340-
const query = this.getQueryByHash<TResult, TError>(resolvedConfig.queryHash)
341-
342-
if (query) {
343-
query.setData(updater)
344-
return
345-
}
346-
347-
this.createQuery({
348-
initialFetched: true,
349-
initialData: functionalUpdate(updater, undefined),
350-
...resolvedConfig,
351-
})
338+
this.buildQuery(queryKey, config).setData(updater)
352339
}
353340
}
354341

src/core/queryObserver.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ export class QueryObserver<TResult, TError> {
1818
private currentResult!: QueryResult<TResult, TError>
1919
private previousQueryResult?: QueryResult<TResult, TError>
2020
private listener?: UpdateListener<TResult, TError>
21-
private initialFetchedCount: number
21+
private initialUpdateCount: number
2222
private staleTimeoutId?: number
2323
private refetchIntervalId?: number
2424

2525
constructor(config: ResolvedQueryConfig<TResult, TError>) {
2626
this.config = config
27-
this.initialFetchedCount = 0
27+
this.initialUpdateCount = 0
2828

2929
// Bind exposed methods
3030
this.clear = this.clear.bind(this)
@@ -161,6 +161,7 @@ export class QueryObserver<TResult, TError> {
161161
if (!this.currentResult.isStale) {
162162
this.currentResult = { ...this.currentResult, isStale: true }
163163
this.notify()
164+
this.config.queryCache.notifyGlobalListeners(this.currentQuery)
164165
}
165166
}, timeout)
166167
}
@@ -229,7 +230,7 @@ export class QueryObserver<TResult, TError> {
229230

230231
// When the query has not been fetched yet and this is the initial render,
231232
// determine the staleness based on the initialStale or existence of initial data.
232-
if (!currentResult && !state.isFetched) {
233+
if (!currentResult && state.isInitialData) {
233234
if (typeof config.initialStale === 'function') {
234235
isStale = config.initialStale()
235236
} else if (typeof config.initialStale === 'boolean') {
@@ -249,10 +250,11 @@ export class QueryObserver<TResult, TError> {
249250
error: state.error,
250251
failureCount: state.failureCount,
251252
fetchMore: this.fetchMore,
252-
isFetched: state.isFetched,
253-
isFetchedAfterMount: state.fetchedCount > this.initialFetchedCount,
253+
isFetched: state.updateCount > 0,
254+
isFetchedAfterMount: state.updateCount > this.initialUpdateCount,
254255
isFetching: state.isFetching,
255256
isFetchingMore: state.isFetchingMore,
257+
isInitialData: state.isInitialData,
256258
isPreviousData,
257259
isStale,
258260
refetch: this.refetch,
@@ -284,7 +286,7 @@ export class QueryObserver<TResult, TError> {
284286

285287
this.previousQueryResult = this.currentResult
286288
this.currentQuery = query
287-
this.initialFetchedCount = query.state.fetchedCount
289+
this.initialUpdateCount = query.state.updateCount
288290
this.updateResult()
289291

290292
if (this.listener) {

src/core/tests/queryCache.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ describe('queryCache', () => {
544544
expect(query.state).toMatchObject({
545545
data: 'data',
546546
isLoading: false,
547-
isFetched: true,
547+
updateCount: 1,
548548
})
549549
})
550550

@@ -578,7 +578,7 @@ describe('queryCache', () => {
578578
expect(query.state).toMatchObject({
579579
data: undefined,
580580
isLoading: false,
581-
isFetched: true,
581+
updateCount: 1,
582582
})
583583
})
584584

src/core/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ export interface BaseQueryConfig<TResult, TError = unknown, TData = TResult> {
4949
queryKeySerializerFn?: QueryKeySerializerFunction
5050
queryFnParamsFilter?: (args: ArrayQueryKey) => ArrayQueryKey
5151
initialData?: TResult | InitialDataFunction<TResult>
52-
initialFetched?: boolean
5352
infinite?: true
5453
/**
5554
* Set this to `false` to disable structural sharing between query results.
@@ -196,10 +195,11 @@ export interface QueryResultBase<TResult, TError = unknown> {
196195
isFetching: boolean
197196
isFetchingMore?: IsFetchingMoreValue
198197
isIdle: boolean
198+
isInitialData: boolean
199199
isLoading: boolean
200+
isPreviousData: boolean
200201
isStale: boolean
201202
isSuccess: boolean
202-
isPreviousData: boolean
203203
refetch: (options?: RefetchOptions) => Promise<TResult | undefined>
204204
status: QueryStatus
205205
updatedAt: number

src/react/tests/useInfiniteQuery.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ describe('useInfiniteQuery', () => {
7272
isFetching: true,
7373
isFetchingMore: false,
7474
isIdle: false,
75+
isInitialData: true,
7576
isLoading: true,
7677
isPreviousData: false,
7778
isStale: true,
@@ -100,6 +101,7 @@ describe('useInfiniteQuery', () => {
100101
isFetchedAfterMount: true,
101102
isFetching: false,
102103
isIdle: false,
104+
isInitialData: false,
103105
isLoading: false,
104106
isPreviousData: false,
105107
isStale: true,

src/react/tests/usePaginatedQuery.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ describe('usePaginatedQuery', () => {
4242
isFetching: true,
4343
isFetchingMore: false,
4444
isIdle: false,
45+
isInitialData: true,
4546
isLoading: true,
4647
isPreviousData: false,
4748
isStale: true,
@@ -66,6 +67,7 @@ describe('usePaginatedQuery', () => {
6667
isFetching: false,
6768
isFetchingMore: false,
6869
isIdle: false,
70+
isInitialData: false,
6971
isLoading: false,
7072
isPreviousData: false,
7173
isStale: true,

src/react/tests/useQuery.test.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ describe('useQuery', () => {
134134
isFetching: true,
135135
isFetchingMore: false,
136136
isIdle: false,
137+
isInitialData: true,
137138
isLoading: true,
138139
isPreviousData: false,
139140
isStale: true,
@@ -156,6 +157,7 @@ describe('useQuery', () => {
156157
isFetching: false,
157158
isFetchingMore: false,
158159
isIdle: false,
160+
isInitialData: false,
159161
isLoading: false,
160162
isPreviousData: false,
161163
isStale: true,
@@ -208,6 +210,7 @@ describe('useQuery', () => {
208210
isFetching: true,
209211
isFetchingMore: false,
210212
isIdle: false,
213+
isInitialData: true,
211214
isLoading: true,
212215
isPreviousData: false,
213216
isStale: true,
@@ -230,6 +233,7 @@ describe('useQuery', () => {
230233
isFetching: true,
231234
isFetchingMore: false,
232235
isIdle: false,
236+
isInitialData: true,
233237
isLoading: true,
234238
isPreviousData: false,
235239
isStale: true,
@@ -252,6 +256,7 @@ describe('useQuery', () => {
252256
isFetching: false,
253257
isFetchingMore: false,
254258
isIdle: false,
259+
isInitialData: true,
255260
isLoading: false,
256261
isPreviousData: false,
257262
isStale: true,
@@ -829,6 +834,28 @@ describe('useQuery', () => {
829834
)
830835
})
831836

837+
it('should notify query cache when a query becomes stale', async () => {
838+
const key = queryKey()
839+
const states: QueryResult<string>[] = []
840+
const fn = jest.fn()
841+
842+
const unsubscribe = queryCache.subscribe(fn)
843+
844+
function Page() {
845+
const state = useQuery(key, () => 'test', {
846+
staleTime: 10,
847+
})
848+
states.push(state)
849+
return null
850+
}
851+
852+
render(<Page />)
853+
854+
await waitForMs(20)
855+
unsubscribe()
856+
expect(fn).toHaveBeenCalledTimes(3)
857+
})
858+
832859
it('should not re-render when a query status changes and notifyOnStatusChange is false', async () => {
833860
const key = queryKey()
834861
const states: QueryResult<string>[] = []

0 commit comments

Comments
 (0)