Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/warm-teeth-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/query-db-collection': minor
---

Add refetchOnWindowFocus and refetchOnReconnect and networkMode configuration options to Query Collections for better control over query refetch behavior
2 changes: 1 addition & 1 deletion packages/db/src/query/builder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ import type {
import type {
CompareOptions,
Context,
GetResult,
FunctionalHavingRow,
GetResult,
GroupByCallback,
JoinOnCallback,
MergeContextForJoinCallback,
Expand Down
28 changes: 27 additions & 1 deletion packages/query-db-collection/src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,27 @@ export interface QueryCollectionConfig<
Array<T>,
TQueryKey
>[`staleTime`]
refetchOnWindowFocus?: QueryObserverOptions<
Array<T>,
TError,
Array<T>,
Array<T>,
TQueryKey
>['refetchOnWindowFocus']
refetchOnReconnect?: QueryObserverOptions<
Array<T>,
TError,
Array<T>,
Array<T>,
TQueryKey
>['refetchOnReconnect']
networkMode?: QueryObserverOptions<
Array<T>,
TError,
Array<T>,
Array<T>,
TQueryKey
>['networkMode']

/**
* Metadata to pass to the query.
Expand Down Expand Up @@ -538,6 +559,9 @@ export function queryCollectionOptions(
retry,
retryDelay,
staleTime,
refetchOnWindowFocus,
refetchOnReconnect,
networkMode,
getKey,
onInsert,
onUpdate,
Expand Down Expand Up @@ -717,7 +741,6 @@ export function queryCollectionOptions(
queryKey: key,
queryFn: queryFunction,
meta: extendedMeta,
structuralSharing: true,
notifyOnChangeProps: `all`,

// Only include options that are explicitly defined to allow QueryClient defaultOptions to be used
Expand All @@ -726,6 +749,9 @@ export function queryCollectionOptions(
...(retry !== undefined && { retry }),
...(retryDelay !== undefined && { retryDelay }),
...(staleTime !== undefined && { staleTime }),
...(refetchOnWindowFocus !== undefined && { refetchOnWindowFocus }),
...(refetchOnReconnect !== undefined && { refetchOnReconnect }),
...(networkMode !== undefined && { networkMode }),
}

const localObserver = new QueryObserver<
Expand Down
317 changes: 317 additions & 0 deletions packages/query-db-collection/tests/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5187,4 +5187,321 @@ describe(`QueryCollection`, () => {
customQueryClient.clear()
})
})

describe(`refetchOnWindowFocus`, () => {
it(`should accept refetchOnWindowFocus as boolean true`, async () => {
const queryKey = [`refetchOnFocus`]
const items: Array<TestItem> = [{ id: `1`, name: `Item 1` }]

const queryFn = vi.fn().mockResolvedValue(items)

const config: QueryCollectionConfig<TestItem> = {
id: `test-focus`,
queryClient,
queryKey,
queryFn,
getKey,
startSync: true,
refetchOnWindowFocus: true,
staleTime: 0,
}

const options = queryCollectionOptions(config)
const collection = createCollection(options)

await vi.waitFor(() => {
expect(queryFn).toHaveBeenCalledTimes(1)
expect(collection.size).toBe(1)
})

expect(collection.get(`1`)).toEqual(items[0])
})

it(`should accept refetchOnWindowFocus as boolean false`, async () => {
const queryKey = [`refetchOnFocusDisabled`]
const items: Array<TestItem> = [{ id: `1`, name: `Item 1` }]

const queryFn = vi.fn().mockResolvedValue(items)

const config: QueryCollectionConfig<TestItem> = {
id: `test-focus-disabled`,
queryClient,
queryKey,
queryFn,
getKey,
startSync: true,
refetchOnWindowFocus: false,
staleTime: 0,
}

const options = queryCollectionOptions(config)
const collection = createCollection(options)

await vi.waitFor(() => {
expect(queryFn).toHaveBeenCalledTimes(1)
expect(collection.size).toBe(1)
})

expect(collection.get(`1`)).toEqual(items[0])
})

it(`should accept refetchOnWindowFocus as function predicate`, async () => {
const queryKey = [`refetchOnFocusPredicate`]
const items: Array<TestItem> = [{ id: `1`, name: `Item 1` }]

const queryFn = vi.fn().mockResolvedValue(items)
const shouldRefetch = vi.fn().mockReturnValue(true)

const config: QueryCollectionConfig<TestItem> = {
id: `test-focus-predicate`,
queryClient,
queryKey,
queryFn,
getKey,
startSync: true,
refetchOnWindowFocus: shouldRefetch as any,
staleTime: 0,
}

const options = queryCollectionOptions(config)
const collection = createCollection(options)

await vi.waitFor(() => {
expect(queryFn).toHaveBeenCalledTimes(1)
expect(collection.size).toBe(1)
})

expect(collection.get(`1`)).toEqual(items[0])
})
})

describe(`refetchOnReconnect`, () => {
it(`should accept refetchOnReconnect as boolean true`, async () => {
const queryKey = [`refetchOnReconnect`]
const items: Array<TestItem> = [{ id: `1`, name: `Item 1` }]

const queryFn = vi.fn().mockResolvedValue(items)

const config: QueryCollectionConfig<TestItem> = {
id: `test-reconnect`,
queryClient,
queryKey,
queryFn,
getKey,
startSync: true,
refetchOnReconnect: true,
staleTime: 0,
}

const options = queryCollectionOptions(config)
const collection = createCollection(options)

await vi.waitFor(() => {
expect(queryFn).toHaveBeenCalledTimes(1)
expect(collection.size).toBe(1)
})

expect(collection.get(`1`)).toEqual(items[0])
})

it(`should accept refetchOnReconnect as boolean false`, async () => {
const queryKey = [`refetchOnReconnectDisabled`]
const items: Array<TestItem> = [{ id: `1`, name: `Item 1` }]

const queryFn = vi.fn().mockResolvedValue(items)

const config: QueryCollectionConfig<TestItem> = {
id: `test-reconnect-disabled`,
queryClient,
queryKey,
queryFn,
getKey,
startSync: true,
refetchOnReconnect: false,
staleTime: 0,
}

const options = queryCollectionOptions(config)
const collection = createCollection(options)

await vi.waitFor(() => {
expect(queryFn).toHaveBeenCalledTimes(1)
expect(collection.size).toBe(1)
})

expect(collection.get(`1`)).toEqual(items[0])
})

it(`should accept refetchOnReconnect as function predicate`, async () => {
const queryKey = [`refetchOnReconnectPredicate`]
const items: Array<TestItem> = [{ id: `1`, name: `Item 1` }]

const queryFn = vi.fn().mockResolvedValue(items)
const shouldRefetch = vi.fn().mockReturnValue(true)

const config: QueryCollectionConfig<TestItem> = {
id: `test-reconnect-predicate`,
queryClient,
queryKey,
queryFn,
getKey,
startSync: true,
refetchOnReconnect: shouldRefetch as any,
staleTime: 0,
}

const options = queryCollectionOptions(config)
const collection = createCollection(options)

await vi.waitFor(() => {
expect(queryFn).toHaveBeenCalledTimes(1)
expect(collection.size).toBe(1)
})

expect(collection.get(`1`)).toEqual(items[0])
})
})

describe(`networkMode`, () => {
it(`should respect networkMode 'always'`, async () => {
const queryKey = [`networkModeAlways`]
const items: Array<TestItem> = [{ id: `1`, name: `Item 1` }]

const queryFn = vi.fn().mockResolvedValue(items)

const config: QueryCollectionConfig<TestItem> = {
id: `test-network-always`,
queryClient,
queryKey,
queryFn,
getKey,
startSync: true,
networkMode: `always`,
}

const options = queryCollectionOptions(config)
const collection = createCollection(options)

await vi.waitFor(() => {
expect(queryFn).toHaveBeenCalledTimes(1)
expect(collection.size).toBe(1)
})

expect(collection.get(`1`)).toEqual(items[0])
})

it(`should respect networkMode 'offlineFirst'`, async () => {
const queryKey = [`networkModeOfflineFirst`]
const items: Array<TestItem> = [{ id: `1`, name: `Item 1` }]

const queryFn = vi.fn().mockResolvedValue(items)

const config: QueryCollectionConfig<TestItem> = {
id: `test-network-offline-first`,
queryClient,
queryKey,
queryFn,
getKey,
startSync: true,
networkMode: `offlineFirst`,
}

const options = queryCollectionOptions(config)
const collection = createCollection(options)

await vi.waitFor(() => {
expect(queryFn).toHaveBeenCalledTimes(1)
expect(collection.size).toBe(1)
})

expect(collection.get(`1`)).toEqual(items[0])
})

it(`should respect networkMode 'online'`, async () => {
const queryKey = [`networkModeOnline`]
const items: Array<TestItem> = [{ id: `1`, name: `Item 1` }]

const queryFn = vi.fn().mockResolvedValue(items)

const config: QueryCollectionConfig<TestItem> = {
id: `test-network-online`,
queryClient,
queryKey,
queryFn,
getKey,
startSync: true,
networkMode: `online`,
}

const options = queryCollectionOptions(config)
const collection = createCollection(options)

await vi.waitFor(() => {
expect(queryFn).toHaveBeenCalledTimes(1)
expect(collection.size).toBe(1)
})

expect(collection.get(`1`)).toEqual(items[0])
})
})

describe(`combined refetch options`, () => {
it(`should work with both refetchOnWindowFocus and refetchOnReconnect enabled`, async () => {
const queryKey = [`combinedRefetch`]
const items: Array<TestItem> = [{ id: `1`, name: `Item 1` }]

const queryFn = vi.fn().mockResolvedValue(items)

const config: QueryCollectionConfig<TestItem> = {
id: `test-combined`,
queryClient,
queryKey,
queryFn,
getKey,
startSync: true,
refetchOnWindowFocus: true,
refetchOnReconnect: true,
staleTime: 0,
}

const options = queryCollectionOptions(config)
const collection = createCollection(options)

await vi.waitFor(() => {
expect(queryFn).toHaveBeenCalledTimes(1)
expect(collection.size).toBe(1)
})

expect(collection.get(`1`)).toEqual(items[0])
})

it(`should work with networkMode and refetch triggers`, async () => {
const queryKey = [`combinedNetworkAndRefetch`]
const items: Array<TestItem> = [{ id: `1`, name: `Item 1` }]

const queryFn = vi.fn().mockResolvedValue(items)

const config: QueryCollectionConfig<TestItem> = {
id: `test-combined-network`,
queryClient,
queryKey,
queryFn,
getKey,
startSync: true,
refetchOnWindowFocus: true,
networkMode: `offlineFirst`,
staleTime: 0,
}

const options = queryCollectionOptions(config)
const collection = createCollection(options)

await vi.waitFor(() => {
expect(queryFn).toHaveBeenCalledTimes(1)
expect(collection.size).toBe(1)
})

expect(collection.get(`1`)).toEqual(items[0])
})
})
})