Skip to content

Commit

Permalink
fix(core): do not rely on dataUpdatedAt truthiness (#7008)
Browse files Browse the repository at this point in the history
* fix(core): do not rely on dataUpdatedAt truthiness

if we want to check for "hasData", we should rather look at state.data and check it against undefined, because it's not possible to have a successful query with data being undefined (since v4)

* chore: fix test that relied on dataUpdatedAt to be set to zero for a query to show up as non-stale
  • Loading branch information
TkDodo committed Mar 2, 2024
1 parent 48b5ab1 commit 3191ae8
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 10 deletions.
8 changes: 4 additions & 4 deletions packages/query-core/src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,15 +250,15 @@ export class Query<
isStale(): boolean {
return (
this.state.isInvalidated ||
!this.state.dataUpdatedAt ||
this.state.data === undefined ||
this.#observers.some((observer) => observer.getCurrentResult().isStale)
)
}

isStaleByTime(staleTime = 0): boolean {
return (
this.state.isInvalidated ||
!this.state.dataUpdatedAt ||
this.state.data === undefined ||
!timeUntilStale(this.state.dataUpdatedAt, staleTime)
)
}
Expand Down Expand Up @@ -329,7 +329,7 @@ export class Query<
fetchOptions?: FetchOptions,
): Promise<TData> {
if (this.state.fetchStatus !== 'idle') {
if (this.state.dataUpdatedAt && fetchOptions?.cancelRefetch) {
if (this.state.data !== undefined && fetchOptions?.cancelRefetch) {
// Silently cancel current fetch if the user wants to cancel refetch
this.cancel({ silent: true })
} else if (this.#promise) {
Expand Down Expand Up @@ -546,7 +546,7 @@ export class Query<
fetchStatus: canFetch(this.options.networkMode)
? 'fetching'
: 'paused',
...(!state.dataUpdatedAt && {
...(state.data === undefined && {
error: null,
status: 'pending',
}),
Expand Down
11 changes: 6 additions & 5 deletions packages/query-core/src/queryObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ export class QueryObserver<
fetchStatus = canFetch(query.options.networkMode)
? 'fetching'
: 'paused'
if (!state.dataUpdatedAt) {
if (state.data === undefined) {
status = 'pending'
}
}
Expand Down Expand Up @@ -536,6 +536,7 @@ export class QueryObserver<
const isError = status === 'error'

const isLoading = isPending && isFetching
const hasData = state.data !== undefined

const result: QueryObserverBaseResult<TData, TError> = {
status,
Expand All @@ -558,10 +559,10 @@ export class QueryObserver<
state.errorUpdateCount > queryInitialState.errorUpdateCount,
isFetching,
isRefetching: isFetching && !isPending,
isLoadingError: isError && state.dataUpdatedAt === 0,
isLoadingError: isError && !hasData,
isPaused: fetchStatus === 'paused',
isPlaceholderData,
isRefetchError: isError && state.dataUpdatedAt !== 0,
isRefetchError: isError && hasData,
isStale: isStale(query, options),
refetch: this.refetch,
}
Expand Down Expand Up @@ -683,7 +684,7 @@ function shouldLoadOnMount(
): boolean {
return (
options.enabled !== false &&
!query.state.dataUpdatedAt &&
query.state.data === undefined &&
!(query.state.status === 'error' && options.retryOnMount === false)
)
}
Expand All @@ -694,7 +695,7 @@ function shouldFetchOnMount(
): boolean {
return (
shouldLoadOnMount(query, options) ||
(query.state.dataUpdatedAt > 0 &&
(query.state.data !== undefined &&
shouldFetchOn(query, options, options.refetchOnMount))
)
}
Expand Down
18 changes: 18 additions & 0 deletions packages/query-core/src/tests/query.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,24 @@ describe('query', () => {
expect(initialDataUpdatedAtSpy).toHaveBeenCalled()
})

test('should work with initialDataUpdatedAt set to zero', async () => {
const key = queryKey()

await queryClient.prefetchQuery({
queryKey: key,
queryFn: () => 'data',
staleTime: Infinity,
initialData: 'initial',
initialDataUpdatedAt: 0,
})

expect(queryCache.find({ queryKey: key })?.state).toMatchObject({
data: 'initial',
status: 'success',
dataUpdatedAt: 0,
})
})

test('queries should be garbage collected even if they never fetched', async () => {
const key = queryKey()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ describe('createPersister', () => {
)

await persisterFn(queryFn, context, query)
query.state.dataUpdatedAt = 0
query.state.data = 'data0'
query.fetch = vi.fn()
expect(query.state.dataUpdatedAt).toEqual(0)

Expand Down

0 comments on commit 3191ae8

Please sign in to comment.